Зачем продолжать вести себя как перерыв в цикле foreach-объект?


если я делаю следующее В сценарии powershell:

$range = 1..100 
ForEach ($_ in $range){
    if ($_ % 7 -ne 0 ) { continue; }
    Write-Host "$($_) is a multiple of 7"
}

Я получаю ожидаемый результат:

7 is a multiple of 7
14 is a multiple of 7
21 is a multiple of 7
28 is a multiple of 7
35 is a multiple of 7
42 is a multiple of 7
49 is a multiple of 7
56 is a multiple of 7
63 is a multiple of 7
70 is a multiple of 7
77 is a multiple of 7
84 is a multiple of 7
91 is a multiple of 7
98 is a multiple of 7

однако, если я использую конвейер и ForEach-Object, продолжить, кажется, вырваться из петли трубопровода.

1..100 | ForEach-Object {
    if ($_ % 7 -ne 0 ) { continue; }
    Write-Host "$($_) is a multiple of 7"
}

мои вопросы: Могу ли я продолжить подобное поведение, все еще делая ForEach-Object, поэтому мне не нужно разбивать мой конвейер?

4 100

4 ответа:

просто использовать return вместо continue. Это return возвращает из блока скрипта, который вызывается ForEach-Object на конкретной итерации, таким образом, она имитирует continue в цикле.

1..100 | ForEach-Object {
    if ($_ % 7 -ne 0 ) { return }
    Write-Host "$($_) is a multiple of 7"
}

это нужно иметь в виду при рефакторинге. Иногда кто-то хочет преобразовать foreach блок оператора в конвейер с ForEach-Object командлет (он даже имеет псевдоним foreach что помогает сделать это преобразование легко и ошибиться легко). Все continue следует заменить на return.

P. S. К сожалению, это не так просто сымитировать break на ForEach-Object.

, потому что For-Each объект-это командлет, а не цикл, и continue / break к нему не применяются.

например, если у вас есть:

$b = 1,2,3

foreach($a in $b){

$a | foreach { if($_ -eq 2) {continue;} else {write-host $_} }

write-host "after"

}

вы получите вывод в виде:

1
after
3
after

это связано с тем, что continue применяется к внешнему циклу foreach, а не к командлету foreach-object. В отсутствие петли, самый внешний уровень, следовательно, давая вам впечатление от него действует как перерыв.

так как же вы получаете продолжение, как поведение? Один из способов-это где-объект, конечно:

1..100 | ?{ $_ % 7  -eq 0} | %{write-host $_ is a mutliple of 7}

другая альтернатива - это своего рода хак, но вы можете обернуть свой блок в цикл, который будет выполняться один раз, таким образом, продолжение будет иметь желаемый эффект:

1..100 | ForEach-Object {
    for($cont=$true;$cont;$cont=$false){
        if ($_ % 7 -ne 0 ) { continue; }
        Write-Host "$($_) is a multiple of 7"
        }
}

простой оператор else заставляет его работать как есть

1..100 | ForEach-Object {
    if ($_ % 7 -ne 0 ) { 
        #do nothing
    } else {
        Write-Host "$($_) is a multiple of 7"
    }
}

или в одном конвейере

1..100 | ForEach-Object { if ($_ % 7 -ne 0 ) {} else {Write-Host "$($_) is a multiple of 7"}}

но более элегантное решение-инвертировать ваш тест и генерировать результат только для ваших успехов

1..100 | ForEach-Object {if ($_ % 7 -eq 0 ) {Write-Host "$($_) is a multiple of 7"}}