Czy potrafisz wyrwać się z „każdego” zamknięcia Groovy?

143

Czy jest możliwe breakz Groovy .each{Closure}, czy powinienem zamiast tego używać klasycznej pętli?

metaliczny
źródło

Odpowiedzi:

194

Nie, nie możesz przerwać „każdego” bez rzucenia wyjątku. Prawdopodobnie chcesz klasycznej pętli, jeśli chcesz, aby przerwa została przerwana w określonych warunkach.

Alternatywnie możesz użyć zamknięcia „find” zamiast each i zwrócić prawdę, gdy zrobiłbyś przerwę.

Ten przykład zostanie przerwany przed przetworzeniem całej listy:

def a = [1, 2, 3, 4, 5, 6, 7]

a.find { 
    if (it > 5) return true // break
    println it  // do the stuff that you wanted to before break
    return false // keep looping
}

Wydruki

1
2
3
4
5

ale nie drukuje 6 lub 7.

Bardzo łatwo jest również napisać własne metody iteratora z niestandardowym zachowaniem przerwania, które akceptuje zamknięcia:

List.metaClass.eachUntilGreaterThanFive = { closure ->
    for ( value in delegate ) {
        if ( value  > 5 ) break
        closure(value)
    }
}

def a = [1, 2, 3, 4, 5, 6, 7]

a.eachUntilGreaterThanFive {
    println it
}

Drukuje również:

1
2
3
4
5    
Ted Naleid
źródło
3
Przesłałem również poprawkę do groovy, która dodaje metodę findResult, która wykonuje zwarte wyszukiwanie, które zwraca pierwszy niezerowy wynik z zamknięcia. Ta metoda może być użyta do pokrycia prawie wszystkich sytuacji, z których ktoś mógłby chcieć wyrwać się wcześniej. Sprawdź poprawkę, aby zobaczyć, czy zostanie zaakceptowana i wrzucona
Ted Naleid
2
Próbowałem w tym przypadku: def a = [1, 2, 3, 4, 5, 6, 7, -1, -2] Zwróciło 1 2 3 4 5 -1 -2. Tak więc „breaK” nie działa.
Phat H. VU
Prawidłowe znalezienie jest właściwą opcją, każdy nigdy nie złamie się po powrocie
Pushkar
jest findlepsze niż any- zobacz inną odpowiedź poniżej od @Michal, która działa dla mnie
Rhubarb
Łatka Teda Naleida do groovy jest bardzo przydatna. Zamiast tego użyj findResult, jeśli faktycznie potrzebujesz wyniku operacji wyszukiwania, a nie samego elementu. Np .: ​def test = [2] test.findResult{ it * 2 }​zwróci 4 zamiast 2
Doug
57

Wymień każdą pętlę na dowolne zamknięcie.

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

    println element

    if (element == 3)
        return true // break
}

Wynik

1
3
Michał Z Muda
źródło
Tylko podstęp. Co się stanie, jeśli po „przerwie” pojawi się oświadczenie. Ta instrukcja będzie nadal wykonywana po spotkaniu "przerwa".
Phat H. VU
2
@Phat H. VU Dodałem zwrot. Oświadczenie po "przerwie" nie zostanie wykonane
Michał Z muda
1
Dzięki za odpowiedź. Masz rację. Głosuję również na twoją odpowiedź. Więcej: metoda any jest zdefiniowana w DefaultGroovyMethods i jest funkcją predykatu, która zwraca wartość true, jeśli element w kolekcji spełnia podane zamknięcie predykatu.
Phat H. VU,
Używanie any()w ten sposób jest nieco mylące, ale z pewnością działa i daje możliwość przerwania lub kontynuowania .
vegemite4me
1
jak @ vegemite4me, myślę, że to „sztuczka”, ale nie rozumiesz dobrze żadnego znaczenia. Ze względu na łatwość konserwacji nie należy używać tego rozwiązania.
MatRt
11

Nie, nie możesz zerwać z zamknięciem w Groovy bez rzucenia wyjątku. Nie należy również używać wyjątków dla przepływu sterowania.

Jeśli okaże się, że chcesz wyrwać się z zamknięcia, prawdopodobnie powinieneś najpierw pomyśleć o tym, dlaczego chcesz to zrobić, a nie jak to zrobić. Pierwszą rzeczą do rozważenia może być zastąpienie omawianego zamknięcia jedną z (konceptualnych) funkcji wyższego rzędu Groovy'ego. Poniższy przykład:

for ( i in 1..10) { if (i < 5) println i; else return}

staje się

(1..10).each{if (it < 5) println it}

staje się

(1..10).findAll{it < 5}.each{println it} 

co również pomaga uzyskać jasność. Znacznie lepiej określa cel twojego kodu.

Potencjalną wadą pokazanych przykładów jest to, że iteracja zatrzymuje się tylko na początku pierwszego przykładu. Jeśli masz wątpliwości dotyczące wydajności, możesz chcieć zatrzymać to od razu.

Jednak w większości przypadków użycia, które obejmują iteracje, można zwykle skorzystać z jednej z metod Groovy'ego: find, grep, collect, inject itp. Zwykle wymagają pewnej „konfiguracji”, a następnie „wiedzą”, jak wykonać iterację za Ciebie, abyś mógł faktycznie uniknąć bezwzględnego zapętlania się, gdy tylko jest to możliwe.

Kai Sternad
źródło
1
„Nie, nie możesz zerwać z zamknięciem w Groovy bez rzucania wyjątku.” To właśnie robi Scala, zobacz dev.bizo.com/2010/01/scala-supports-non-local-returns.html
OlliP
2

Używam tylko specjalnego zamknięcia

// 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
}
morze-kg
źródło
3
a jeśli masz 1 miliard wierszy, a zamknięcie wewnętrzne zwraca prawdę przy pierwszym wywołaniu, to wykonasz iterację ponad 1 miliard minus jedna wartość. :(
sbglasius
-4

(1..10) .each {

jeśli (to <5)

wydrukuj to

jeszcze

return false

Sagar Mal Shankhala
źródło
2
To nie wyrywa się z each, po prostu nie wypisuje wartości większych niż 4. elseJest zbędne, twój kod zrobiłby to samo bez niego. Możesz również udowodnić, eachże nie zrywa, return falsejeśli umieścisz println "not breaking"tuż po elsei tuż przed return false.
Stefan van den Akker
Zrób przynajmniej minimum wysiłku, aby sformatować swój kod pod kątem czytelności. Kiedy odpowiadasz, pojawia się podgląd wizualny i wiele instrukcji, jak to zrobić
Mateusz Was
-11

Możesz się przedrzeć RETURN. Na przykład

  def a = [1, 2, 3, 4, 5, 6, 7]
  def ret = 0
  a.each {def n ->
    if (n > 5) {
      ret = n
      return ret
    }
  }

Mi to pasuje!

Tseveen D
źródło
6
O to właśnie pytał OP, chociaż oczywiście nie o to mu chodziło.
Szocske
Czy mogę opisać, dlaczego ma to negatywne głosy. Wydaje mi się, że jest to ta sama koncepcja, co mówi najlepsza odpowiedź (z mniejszym wyjaśnieniem) z +133 głosami.
Skepi
1
@Skepi Nie możesz "złamać" powyżej zamknięcia. Możesz przerwać anymetodę tablicy, zwracając false. Nie możesz złamać eachmetody w ten sam sposób.
Nux