2010-01-19 3 views
4

Я написал это (который работает):Powershell эквивалент F # Seq.forall

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 

Но то, что я хочу сделать, это заменить ($ _ -lt 1) с функцией называется что-то вроде $ предикат, который я передаю в функцию ForAll. Кажется, я не могу заставить это работать. Есть идеи?

ответ

3

Использование [scriptblock], imho намного проще, чем использование функций здесь. В этой задаче были созданы скриптовые блоки.

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 

$ арг [0] обозначает первый аргумент, передаваемый в ScriptBlock - в данном случае это $ _.

+0

Stej, я замечаю в этом, что [ScriptBlock] на самом деле не требуется, если $ _ заменяется на $ арг [ 0]. В ту ночь я был очень смущен, пытаясь заставить это работать. В конце концов, самая короткая версия, которую я получил, - это просто небольшое изменение на вашем. функция ForAll ($ сказуемое) {...} $ ALIST = (0..4) $ Blist = (1..4) $ ALIST | ForAll {$ _ -gt 0} # возвращает false $ bList | ForAll {$ _ -gt 0} # возвращает true $ bList | ForAll {$ _ -gt 0- и $ _ -lt 5} # возвращает true –

+0

Да, это определенно верно. Вы можете использовать автоматическую переменную - она ​​выглядит так же, как командлет foreach-object. Однако у меня были проблемы с более сложными сценариями с $ _, поэтому я не решаюсь использовать $ _. – stej

3

Если вы на PowerShell 2.0, вы можете использовать параметры с ScriptBlock объявить параметры, например:

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 
+1

Я всегда хотел видеть правильную обработку параметра, который может быть из конвейера и как аргумент. Я запомню трюк с предсказанием. Есть ли какая-то причина, почему бы не трубить его? $ InputObject | % {if (! (& $ Expression $ _)) {$ allTrue = $ false}} } – stej

+0

Nope - это будет работать так же хорошо. Когда я пишу функции, я склоняюсь назад к своим C# (не конвейерным) способам для циклов. :-) Небольшие конвейеры достаточно просты для чтения, но когда они продолжаются для линии после строки, я нахожу явные петли более легкими для чтения, когда возвращаюсь к скрипту. –

+0

Я не понимаю, зачем вам нужен foreach? Я думал, что процесс должен принимать отдельные значения из конвейера? Является ли $ InputObject привязанным к одному элементу внутри процесса? (из-за атрибута), если это так, почему использование begin, process, end вообще? – Jake

0

Для эффективной обработки мы будем использовать фильтр вместо функции, и мы были бы «перерыв», как как только мы нашли ложный элемент. Это также останавливает все вверх по течению процесса тоже:

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

Используйте его идиоматически:

(0..4) | ForAll { $_ -gt 0 }  # returns false 
(1..4) | ForAll { $_ -gt 0 }  # returns true 
Смежные вопросы