Neden bir Foreach-Nesnesinde 'devam etmek' 'kopmak' gibi davranır?


123

Bir PowerShell komut dosyasında aşağıdakileri yaparsam:

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

Beklenen çıktıyı alıyorum:

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

Ben bir boru hattı kullanmak ve Ancak, ForEach-Object, continueboru hattı döngünün patlak gibi görünüyor.

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

continueHalen ForEach-Object yaparken benzer bir davranış elde edebilir miyim , böylece ardışık düzenimi bölmek zorunda kalmaz mıyım?


Kullanabileceğiniz birçok komutun bulunduğu bir sayfa foreach: techotopia.com/index.php/…
bgmCoder

Burada makul bir açıklama ve örnek buldum
Nathan Hartley

Yanıtlar:


164

returnBunun yerine basitçe kullanın continue. Bu return, ForEach-Objectbelirli bir yinelemede çağrılan komut dosyası bloğundan geri döner , böylece continuebir döngüde simüle eder .

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

Yeniden düzenleme yaparken akılda tutulması gereken bir sorun vardır. Bazen bir kişi foreachbir ForEach-Objectcmdlet ile bir ifade bloğunu bir ardışık düzene dönüştürmek ister ( foreachbu dönüşümü kolaylaştırmaya ve hataları da kolaylaştırmaya yardımcı olan takma ada sahiptir ). Tüm continues ile değiştirilmelidir return.

Not: Ne yazık ki, simülasyonu breakyapmak o kadar kolay değil ForEach-Object.


2
Görünüşe göre OP'nin söylediğine göre continuebir breakin simüle etmek için kullanılabilir ForEach-Object:)
Richard Hauer

6
@ Richard Hauer Böyle bir continue, sadece ForEach-Objectkullanıldığı yerde değil, tüm betiği bozacaktır .
Roman Kuzmin

22

Çünkü For-Eachnesne cmdlet ve bir çıkışı ve continueve breakbuna geçerli değildir.

Örneğin, varsa:

$b = 1,2,3

foreach($a in $b) {

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

    Write-Host  "after"
}

Çıktı şu şekilde alacaksınız:

1
after
3
after

Bunun nedeni, continueget'lerin foreach-object cmdlet'ine değil dış foreach döngüsüne uygulanmasıdır. Bir döngünün yokluğunda, en dış düzey, bu nedenle size onun gibi davrandığına dair bir izlenim verir break.

Öyleyse nasıl benzer bir continuedavranış elde edersiniz ? Bir yol, Elbette Nerede-Nesne'dir :

1..100 | ?{ $_ % 7  -eq 0} | %{Write-Host $_ is a multiple of 7}

Where-Object cmdlet'ini kullanmak iyi bir öneridir. Gerçek durumumda, if ifadesinden önce gelen birden çok kod satırını okunması zor tek bir uzun satıra dönüştürmenin mantıklı olduğunu düşünmüyorum. Ancak, bu benim için başka durumlarda işe yarar.
Justin Dearing

@JustinDearing - In my actual case, I don't think it makes sense to make the multiple lines of code preceding my if statement into a single long line of hard to read code.Ne demek istiyorsun?
manojlds

3
@manojlds belki sizin tek satırlık çözümünüzün "okunması zor" olduğunu düşünüyor, en azından benim için tam tersi. İşleri yapmanın boru hattı yolu gerçekten güçlü ve nettir ve bunun gibi basit şeyler için doğru yaklaşımdır. Bundan yararlanmadan kabukta kod yazmak anlamsızdır.
mjsr

Benim durumumda bu doğru cevaptı, devam etme veya geri dönme yapacağım nesneleri filtrelemek için bir nerede koşulu ekleyin, böylece ilk etapta onları işlememe gerek kalmaz. +1
Chris Magnuson

3

Başka bir alternatif de bir tür hack'tir, ancak bloğunuzu bir kez çalıştırılacak bir döngüye sarabilirsiniz. Bu şekilde, continueistenen etkiye sahip olacaktır:

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

4
Açıkçası, bu çirkin :) Ve sadece hack değil çünkü foreach nesnesi yerine foreach döngüsü de kullanabilirdiniz.
manojlds

1
@manojlds: 1..100 yalnızca gösterim amaçlıdır. do {} while ($ False) aynı döngü için olduğu kadar çalışır ve biraz daha sezgiseldir.
Harry Martyrossian

2

Basit bir elseifade, aşağıdaki gibi çalışmasını sağlar:

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

Veya tek bir boru hattında:

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

Ancak daha zarif bir çözüm, testinizi tersine çevirmek ve yalnızca başarılarınız için çıktı üretmektir.

1..100 | ForEach-Object {if ($_ % 7 -eq 0 ) {Write-Host "$($_) is a multiple of 7"}}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.