Почему "continue" ведет себя как "break" в объекте 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
, continue
, похоже, выходит из цикла конвейера.
1..100 | ForEach-Object {
if ($_ % 7 -ne 0 ) { continue; }
Write-Host "$($_) is a multiple of 7"
}
Могу ли я получить поведение continue
-like, все еще работая с ForEach-Object, чтобы мне не пришлось разбивать мой конвейер?
Ответы
Ответ 1
Просто используйте 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
.
П.С.: К сожалению, не так-то просто смоделировать break
в ForEach-Object
.
Ответ 2
Поскольку объект 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. В отсутствие цикла, самого внешнего уровня, следовательно, создается впечатление, что он действует как break
.
Итак, как вы получаете поведение continue
-like? Одним из способов является Where-Object, конечно:
1..100 | ?{ $_ % 7 -eq 0} | %{Write-Host $_ is a multiple of 7}
Ответ 3
Другой альтернативой является своего рода хак, но вы можете заключить свой блок в цикл, который будет выполняться один раз. Таким образом, continue
даст желаемый эффект:
1..100 | ForEach-Object {
for ($cont=$true; $cont; $cont=$false) {
if ($_ % 7 -ne 0 ) { continue; }
Write-Host "$($_) is a multiple of 7"
}
}
Ответ 4
Простое утверждение 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"}}