Можете ли вы оторваться от Groovy «каждый» замыкание?

143

Это возможно breakиз Groovy .each{Closure}, или я должен вместо этого использовать классический цикл?

оловоносный
источник

Ответы:

194

Нет, вы не можете прервать «каждый» без исключения. Скорее всего, вам нужен классический цикл, если вы хотите, чтобы разрыв прерывался при определенных условиях.

В качестве альтернативы вы можете использовать замыкание «найти» вместо «каждый» и возвращать значение «истина», когда вы сделали перерыв.

Этот пример будет прерван до обработки всего списка:

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
}

Печать

1
2
3
4
5

но не печатает 6 или 7.

Также очень легко написать свои собственные методы итератора с пользовательским поведением разрыва, которые принимают замыкания:

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
}

Также печатает:

1
2
3
4
5    
Тед Нейлид
источник
3
Я также представил патч для groovy, который добавляет метод findResult, который выполняет поиск с коротким замыканием, который возвращает первый ненулевой результат из замыкания. Этот метод может быть использован для охвата почти всех ситуаций, которые кто-то может захотеть вырваться из каждого рано. Проверьте патч, чтобы увидеть, будет ли он принят и превращен в отличный (я надеюсь на 1.8): jira.codehaus.org/browse/GROOVY-4253
Тед Нейлид
2
Я пробовал этот случай: def a = [1, 2, 3, 4, 5, 6, 7, -1, -2] Возвращено 1 2 3 4 5 -1 -2. Итак, «breaK» не работает.
Phat H. VU
Правильный поиск - правильный вариант, каждый никогда не сломается при возврате
Пушкарь
это findлучше , чем any- см другой ответ ниже от @Michal , что один работает для меня
Ревень
Патч Теда Нейлида к заводному очень полезен. Вместо этого используйте findResult, если вам действительно нужен результат операции поиска, а не сам элемент. Пример: ​def test = [2] test.findResult{ it * 2 }​вернет 4 вместо 2
Дуг
57

Замените каждую петлю любым замыканием.

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

    println element

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

Вывод

1
3
Михал З муда
источник
Просто трюк. Что произойдет, если после «перерыва» появится заявление? Это заявление будет по-прежнему подвергаться действию после встречи "перерыв".
Phat H. VU
2
@Phat H. VU я добавил возврат. Заявление после «перерыва» не будет исполнено
Михал З Муда
1
Спасибо за ваш ответ. Ты прав. Я также голосую за ваш ответ. Более того: метод any определен в DefaultGroovyMethods, и это функция предиката, которая возвращает true, если элемент в коллекции удовлетворяет предоставленному закрытию предиката.
Phat H. VU
Использование any()таким образом немного вводит в заблуждение, но оно, безусловно, работает и дает вам возможность сломаться или продолжить .
vegemite4me
1
как @ vegemite4me, я думаю, что это «трюк», но вы плохо понимаете смысл любого. Для удобства обслуживания не следует использовать это решение.
MatRt
11

Нет, вы не можете выйти из замыкания в Groovy без исключения. Кроме того, вы не должны использовать исключения для потока управления.

Если вы обнаружите, что хотите выйти из замыкания, вам, вероятно, следует сначала подумать о том, почему вы хотите это сделать, а не о том, как это сделать. Первым, что следует рассмотреть, может быть замена рассматриваемого замыкания одной из (концептуальных) функций Groovy более высокого порядка. Следующий пример:

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

становится

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

становится

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

что также помогает ясности. В нем гораздо лучше изложены цели вашего кода.

Потенциальный недостаток в показанных примерах состоит в том, что итерация останавливается только рано в первом примере. Если у вас есть соображения производительности, вы можете остановить это прямо сейчас.

Однако для большинства случаев использования, включающих итерации, обычно можно прибегнуть к одному из методов Groovy: поиск, grep, collect, inject и т. Д. Они обычно принимают некоторую «конфигурацию», а затем «знают», как выполнить итерацию за вас, чтобы вы могли избежать императивных циклов, где это возможно.

Кай Стернад
источник
1
«Нет, вы не можете выйти из замыкания в Groovy, не создав
OlliP
2

Просто используя специальное закрытие

// 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
и если у вас есть 1 миллиард строк, и внутреннее замыкание возвращает true при первом вызове, то вы будете повторять более 1 миллиарда минус одно значение. :(
sbglasius
-4

(1..10) .each {

если (это <5)

распечатайте это

еще

вернуть ложь

Сагар Мал Шанхала
источник
2
Это не выходит из строя each, оно просто не печатает значения больше 4. Это elseизлишне, ваш код сделал бы то же самое без него. Кроме того, вы можете доказать, eachчто не порвется, return falseесли вы поставите println "not breaking"сразу после elseи только до этого return false.
Стефан ван ден Аккер
Пожалуйста, сделайте хотя бы минимальные усилия для форматирования вашего кода для удобства чтения. Когда вы отвечаете, есть визуальный предварительный просмотр и множество инструкций, как это сделать
Mateusz Was
-11

Вы могли бы прорваться RETURN. Например

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

Меня устраивает!

Цевеен Д
источник
6
Это именно то, что спросил ОП, хотя, очевидно, он не это имел в виду.
Щоцке
Могу ли я получить описание, почему это имеет отрицательные голоса. Мне кажется, что это та же концепция, что и в верхнем ответе (с меньшим количеством объяснений) с +133 голосами.
Skepi
1
@Skepi Вы не можете "сломаться" над закрытием. Вы можете сломать anyметод массива, вернув false. Вы не можете нарушить eachметод таким же образом.
Nux