tags:

views:

61

answers:

3

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 # 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?

+2  A: 

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 $_.

stej
Stej, I notice in this that the [scriptblock] isn't actually required if $_ is substituted for $args[0]. I was getting myself quite confused the other night trying to get this to work. In the end the shortest version I've got is just a slight change on yours.function ForAll($predicate) { ...} $aList = (0..4) $bList = (1..4) $aList | ForAll {$_ -gt 0 } # returns false $bList | ForAll {$_ -gt 0 } # returns true $bList | ForAll {$_ -gt 0 -and $_ -lt 5 } # returns true
Bohdan Szymanik
Yes, it's definitelly true. You may use the automatic variable - it looks the same as foreach-object cmdlet. However, I had in some more complicated scripts problems with $_, so I hesitate to use $_.
stej
+2  A: 

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
Keith Hill
stej
Nope - that would work just as well. When I write functions I tend to fall back into my C# (non-pipelined) ways for loops. :-) Small pipelines are easy enough to read but when they go on for line after line, I find explicit loops easier to read when I come back to the script.
Keith Hill