2009-08-26 9 views

Odpowiedz

8

Można wyjątek:

try { 
    [1, 2, 3].each { 
     println(it) 
     if (it == 2) 
      throw new Exception("return from closure") 
    } 
} catch (Exception e) { } 

użycie może również użyć „FindAll” lub „grep” odfiltrować listę, a następnie użyć „każdy ".

[1, 2, 3].findAll{ it < 3 }.each{ println it } 
+8

Czy nie zgodziłbyś się, że używanie wyjątków do sterowania przepływem jest zwykle uważane za zły wybór lub zapach kodu? –

+1

Tak. Ale jest to sposób na wyjście z zamknięcia. Oczywiście zwykle istnieją bardziej odpowiednie metody, takie jak znajdowanie lub znajdowanie wszystkiego. –

12

12/05/2013 Mocno edytowane.

Odpowiedzi na pytanie, które zadano.

Czy możliwe jest wyjście z zamknięcia?

można byłoby "break" Spośród zamknięcia wydając słowa kluczowego return. Jednak nie jest to pomocne w podanym przykładzie. Powodem tego jest to, że zamknięcie (myśl o nim jako metodzie) jest wywoływane przez metodę each dla każdego elementu w kolekcji.

Jeżeli Państwo uruchomić ten przykład widać to wydrukować 1 to 3.

[1, 2, 3].each { 
    if (it == 2) return 
    println(it) 
} 

Dlaczego break w kontekście each nie ma sensu.

Aby zrozumieć, dlaczego nie można wykluczyć metody each, tak jakbyś mógł zerwać z pętli for, musisz zrozumieć trochę tego, co się naprawdę dzieje. Oto ogromne uproszczenie, jakie ma każda metoda w kolekcji.

myEach([0,1,3]) 

void myEach(List things) { 
    for (i in things) { 
     myEachMethod(i) 
    } 
} 

void myEachMethod(Object it) { // this is your Closure 
    if(it == 2) return 
    println it 
} 

Jak widać zamknięcie jest w zasadzie metodą, którą można przekazać. Podobnie jak w Javie, nie można oderwać się od wywołania metody lub zamknięcia.

Co zrobić zamiast zerwania z each.

W Groovy powinieneś wyrażać swój kod używając abstrakcji wysokiego poziomu, ponieważ taka prymitywna pętla nie jest idiomatyczna. Na przykład, który podałeś rozważałbym skorzystanie z findAll. Na przykład:

[1,2,3].findAll { it < 2 }.each { println it } 

Mam nadzieję, że pomoże to zrozumieć, co się dzieje.

Odpowiedzi na domniemane pytanie.

Czy można zerwać z iteracji Collection.each przeciwko dostarczonego zamknięcia?

Nie można wykluczyć metody each bez rzucania i łapania wyjątków, jak powiedział John Wagenleitner. Chociaż twierdzę, że rzucanie i łapanie wyjątków w imię kontroli przepływu jest zapachem kodu, a inny programista może uderzyć cię w dłonie.

+0

Twój kod drukuje 1, 2, 3 - powrót nie powróci z zamknięcia. –

+1

Chciałem tylko wyjaśnić, że powrót nie "wyrwie się" z zamknięcia, po prostu zwróci metodę wykonującą zamknięcie. W powyższym przypadku, gdyby był wydruk poniżej zwrotu, wydrukowałby on tylko 1 i 3, jednak 1, 2 i 3 nadal były drukowane przez pierwszy println. –

+0

Myślę, że jesteś zdezorientowany. "każdy" jest METODĄ, która przyjmuje argument jako zamknięcie. Następnie wykonuje iterację nad zbiorem wywołującym zamknięcie i przekazując bieżący obiekt jako argument do zamknięcia. Zaktualizowałem moją odpowiedź, aby było to bardziej jasne. –

21

Często zapominam, że Groovy stosuje metodę "any".

[1, 2, 3].any 
{ 
    println it 
    return (it == 2) 
}​ 
2

Jest to poparcie dla odpowiedzi Johna Wagenleitera. Odpowiedź Tigerizzy'ego jest całkiem błędna. Z łatwością można go praktycznie obalić, wykonując swoją pierwszą próbkę kodu lub teoretycznie czytając dokumentację Groovy. A return zwraca wartość (lub wartość zerową bez argumentu) z bieżącej iteracji, ale nie zatrzymuje iteracji. W zamknięciu zachowuje się raczej jak kontynuuje.

Nie będziesz w stanie użyć wstrzyknięcia bez zrozumienia.

Nie ma sposobu, aby "przerwać pętlę", z wyjątkiem sytuacji, gdy wystąpi wyjątek. Używanie wyjątków w tym celu jest uważane za śmierdzące. Tak więc, jak sugeruje Wagenleiter, najlepszą praktyką jest odfiltrowanie elementów, które chcesz powtórzyć przed uruchomieniem każdego lub jednego z kuzynów.

7

spróbuje użyć dowolny zamiast każdy

def list = [1, 2, 3, 4, 5, -1, -2] 
list.any { element -> 
    if (element > 3) 
    return true // break 
    println element 
} 

Rezultat: 1, 2, 3

1

tylko za pomocą specjalnego Zamknięcie

// declare and implement: 
def eachWithBreak = { list, Closure c -> 
    boolean bBreak = false 
    list.each() { it -> 
    if (bBreak) return 
    bBreak = c(it) 
    } 
} 

def list = [1,2,3,4,5,6] 
eachWithBreak list, { it -> 
    if (it > 3) return true // break 'eachWithBreak' 
    println it 
    return false // next it 
} 
0

Z rx-java ty może przekształcić wartość iterowalną w obsesję RVable.

Następnie można zastąpić kontynuować z filtra i przerwie z takeWhile

Oto przykład:

import rx.Observable 

Observable.from(1..100000000000000000) 
      .filter { it % 2 != 1} 
      .takeWhile { it<10 } 
      .forEach {println it} 
2

Jest inne rozwiązanie. Chociaż te fajne rzeczy, takie jak each/find/any, są całkiem fajne: jeśli to nie pasuje, nie używaj tego. Nadal można jednak używać zwykłego starego, zwykłego, Teraz możesz swobodnie używać kontynuacji/break/return, jak chcesz. Wynikowy kod może nie być cool, ale jest łatwy i zrozumiały.

+0

Poszedłbym dalej: użyj najprostszego możliwego rozwiązania, chyba że widzisz powód, dlaczego nie. Jeśli jesteś super-biegły w Groovy, możesz użyć 'jakiegokolwiek' tutaj z 'return true' by być tak prostym jak ten. Ale zamknięcia są takie, że są zaprojektowane tak, aby można je było ominąć i manipulować nimi. W przypadku zwykłej pracy proste narzędzie będzie dobrze działać! –

Powiązane problemy