开发者

Powershell equivalent to F# Seq.forall

I've written this (which works):

function ForAll {
    BEGIN {
        $allTrue = $true
    }
    PROCESS {
        if ($_ -lt 1) { $allTrue = $false }  
    }
    END {
        $allTrue
    }
}

$aList = (0..4)
$bList = (1..4)

$aList | ForAll # 开发者_JS百科returns false
$bList | ForAll # returns true

But what I want to do is replace the ($_ -lt 1) with a function called something like $predicate that I pass into the ForAll function. I can't seem to get this to work. Any ideas?


Use [scriptblock], imho it is much easier than using functions here. That's the task why scriptblocks were invented.

function ForAll([scriptblock]$predicate) {

    BEGIN {
        $allTrue = $true
    }
    PROCESS {
        if (!(& $predicate $_)) { $allTrue = $false }  
    }
    END {
        $allTrue
    }
}

$aList = (0..4)
$bList = (1..4)

$aList | ForAll {$args[0] -le -10 } # returns false
$bList | ForAll {$args[0] -le 10 } # returns true

$args[0] denotes the first argument passed to the scriptblock - in this case it is $_.


If you're on PowerShell 2.0, you can use param with a scriptblock to declare parameters e.g.:

function ForAll {
    param(
        [Parameter(Position=0,Mandatory=$true)]
        [scriptblock]
        $Expression,

        [Parameter(Position=1,Mandatory=$true,ValueFromPipeline=$true)]
        [psobject]
        $InputObject
    ) 

    Begin { $allTrue = $true } 

    Process { 
        foreach ($obj in $InputObject) {
            if (!(&$Expression $obj)) { $allTrue = $false }
        }
    } 

    End { $allTrue } 
} 

$aList = (0..4) 
$bList = (1..4) 

ForAll {param($val) $val -gt 0} (0..4) # returns false 

$aList | ForAll {param($val) $val -gt 0} # returns false 
$bList | ForAll {param($val) $val -gt 0} # returns true 
$aList | ForAll {param($val) $val -ge 0 -and $val -le 4} # returns true


For efficient processing we would use a filter instead of a function and we would "break" as soon as we found the spurious element. This also stops all upstream process too:

filter ForAll([scriptblock] $predicate) {
    PROCESS {
        if ( @($_ | Where-Object $predicate).Length -eq 0 ) {
            $false;
            break;
        }
    }
    END {
        $true;
    }
}

Use it idiomatically:

(0..4) | ForAll { $_ -gt 0 }     # returns false
(1..4) | ForAll { $_ -gt 0 }     # returns true
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜