Можно ли вырваться из замыкания в заводной - PullRequest
25 голосов
/ 26 августа 2009

есть ли способ «вырваться» из заводной укупорки.

может быть что-то вроде этого:

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

Ответы [ 9 ]

21 голосов
/ 05 июля 2010

Я часто забываю, что Groovy реализует «любой» метод.

[1, 2, 3].any
{   
   println it
   return (it == 2)
}​
13 голосов
/ 28 августа 2009

12/05/2013 Сильно отредактировано.

Ответ на заданный вопрос.

Можно ли вырваться из замыкания?

Вы бы "сломали" из закрытия, введя ключевое слово return. Однако это не помогает в приведенном примере. Причина этого заключается в том, что замыкание (представьте его как метод) вызывается методом each для каждого элемента в коллекции.

Если вы запустите этот пример, вы увидите, что он напечатает 1, а затем 3.

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

Почему break в контексте each не имеет смысла.

Чтобы понять, почему вы не можете выйти из метода each, как если бы вы могли выйти из цикла for, вам нужно немного понять, что на самом деле происходит. Вот грубое упрощение того, что делает каждый метод в коллекции.

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
}

Как видите, замыкание - это в основном метод, который можно обойти. Как и в Java, вы не можете разорвать вызов метода или замыкание.

Что делать вместо взлома от each.

В Groovy вы должны выражать свой код с помощью высокоуровневых абстракций, поскольку такие примитивные циклы не являются идиоматическими. Для примера, который вы привели, я бы рассмотрел использование findAll. Например:

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

Надеюсь, это поможет вам понять, что происходит.

Ответ на подразумеваемый вопрос.

Можете ли вы выйти из Collection.each итераций против поставляемого замыкания?

Вы не можете выйти из метода each, не бросив и не поймав исключение, как сказал Джон Вагенляйтнер. Хотя я бы сказал, что выбрасывание и перехват исключения во имя управления потоком - это запах кода, и один из программистов может хлопнуть вас по рукам.

8 голосов
/ 27 августа 2009

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

8 голосов
/ 27 августа 2009

Вы можете выбросить исключение:

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

Использование также может использовать «findAll» или «grep», чтобы отфильтровать ваш список, а затем использовать «each».

[1, 2, 3].findAll{ it < 3 }.each{ println it }
7 голосов
/ 09 декабря 2013

Попробуйте использовать любой вместо каждый

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

Результат: 1, 2, 3

3 голосов
/ 07 сентября 2017

Есть другое решение. Хотя такие классные вещи, как каждый / найти / любой, довольно крутые: если они не подходят, не используйте их. Вы все еще можете использовать старый добрый

for (def element : list)

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

2 голосов
/ 04 марта 2014

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

// 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
}
2 голосов
/ 19 мая 2011

Это в поддержку ответа Джона Вагенлейтера. Ответ Тигериззи совершенно неправильный. Это можно легко опровергнуть практически, выполнив его первый пример кода, или теоретически, прочитав документацию Groovy. return возвращает значение (или ноль без аргумента) из текущей итерации, но не останавливает итерацию. В закрытии он ведет себя скорее как continue .

Вы не сможете использовать inject , не понимая этого.

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

0 голосов
/ 02 июня 2015

С rx-java вы можете преобразовать итерацию в наблюдаемую.

Затем вы можете заменить continue на filter и break на takeWhile

Вот пример:

import rx.Observable

Observable.from(1..100000000000000000)
          .filter { it % 2 != 1} 
          .takeWhile { it<10 } 
          .forEach {println it}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...