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?

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    

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

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.


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
}

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


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!


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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.