web-dev-qa-db-fra.com

Pourquoi continue se comporte comme une rupture dans un objet Foreach?

Si je fais ce qui suit dans un script PowerShell:

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

Je reçois le résultat attendu de:

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

Cependant, si j'utilise un pipeline et ForEach-Object, continue semble sortir de la boucle de pipeline.

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

Ma question est la suivante: puis-je avoir un comportement semblable à celui que je garde tout en continuant à faire ForEach-Object, afin de ne pas avoir à rompre mon pipeline?

105
Justin Dearing

Utilisez simplement la return au lieu de la continue. Cette return retourne du bloc de script appelé par ForEach-Object lors d'une itération particulière. Elle simule donc la continue dans une boucle.

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

Il faut garder cela à l’esprit lors de la refactorisation. Parfois, on veut convertir un bloc d'instructions foreach en un pipeline avec une applet de commande ForEach-Object (il a même le pseudonyme foreach qui facilite la conversion et les erreurs, aussi). Tous les continue doivent être remplacés par return.

P.S. Malheureusement, il n’est pas si facile de simuler break dans ForEach-Object.

139
Roman Kuzmin

Parce que For-Each objet est une applet de commande et non une boucle et continuer/interrompre ne lui est pas applicable.

Par exemple, si vous avez:

$b = 1,2,3

foreach($a in $b){

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

write-Host "after"

}

vous obtiendrez une sortie en tant que:

1
after
3
after

C'est parce que la commande continue est appliquée à la boucle externe foreach et non à l'applet de commande foreach-object. En l’absence de boucle, le niveau le plus à l’extérieur vous donne l’impression qu’il agit comme une pause.

Alors, comment obtenez-vous continuer comme comportement? Une façon est où-objet de cours:

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

Une autre alternative est une sorte de bidouille, mais vous pouvez envelopper votre bloc dans une boucle qui s'exécutera une fois, ainsi continuer aura l'effet désiré:

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

Une simple déclaration else le fait fonctionner tel quel

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

ou dans un seul pipeline

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

mais une solution plus élégante consiste à inverser votre test et à générer une sortie uniquement pour vos succès.

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