Чи можете ви відірватися від закритого закриття "кожного"?


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. Отже, "брак" не працює.
Phat H. VU

Правильна знахідка - правильний варіант, кожен не повернеться поверненням
Pushkar

це 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 Я додав повернення. Заява після "перерви" не буде виконана
Michal Z muda

1
Спасибі за Вашу відповідь. Ти маєш рацію. Я також голосую за вашу відповідь. Більше: будь-який метод визначений у DefaultGroovyMethods і це функція предиката, яка повертає істину, якщо елемент у колекції задовольняє наданому закриттю предиката.
Phat H. VU

Використання any()цього способу трохи вводить в оману, але це, безумовно, спрацьовує і дає можливість зламати або продовжувати .
vegemite4me

1
як, наприклад, @ vegemite4me, я думаю, що це "фокус", але ви добре не розумієте сенсу будь-якого. Для підтримання технічного обслуговування не слід використовувати це рішення.
MatRt

11

Ні, ви не можете зірватися з закриття в 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} 

що також сприяє ясності. У ньому йдеться про наміри вашого коду набагато краще.

Потенційний недолік показаних прикладів полягає в тому, що ітерація зупиняється лише на початку першого прикладу. Якщо у вас є міркування щодо продуктивності, можливо, ви захочете зупинити його тут і там.

Однак для більшості випадків використання, що стосуються ітерацій, зазвичай можна вдатися до одного з методів пошуку, грепування, збирання, введення тощо. Зазвичай вони приймають певну "конфігурацію", а потім "знають", як зробити ітерацію для вас, щоб ви могли фактично уникнути необхідних циклів, коли це можливо.


1
"Ні, ви не можете зірватися з закриття в Groovy, не кидаючи виняток." Це те, що робить Scala, див. Dev.bizo.com/2010/01/scala-supports-non-local-returns.html
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 мільярд рядків, а внутрішнє закриття повернеться істинним під час першого дзвінка, ви повторите більше 1 мільярда мінус одне значення. :(
sbglasius

-4

(1..10) .each {

якщо (це <5)

println це

ще

повернути хибне


2
Це не виходить із each, просто не надрукує значення більше 4. Це elseзайве, ваш код зробив би те саме без нього. Крім того, ви можете довести, eachщо не порушується, return falseякщо ставити println "not breaking"відразу після elseі безпосередньо перед цим return false.
Стефан ван ден Аккер

Будь ласка, докладіть мінімум зусиль, щоб відформатувати свій код для читабельності. Існує візуальний попередній перегляд, коли ви відповідаєте і багато інструкцій, як це зробити
Матеуш був

-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
Це саме те, що запитував ОП, хоча це, очевидно, не те, що він мав на увазі.
Sоcske

Чи можу я описати, чому за це негативні голоси. Мені це здається такою ж концепцією, що і те, що говорить відповідь (з меншим поясненням) +133 голосами.
Скепі

1
@Skepi Ви не можете "пробитися" над закриттям. Ви можете порушити anyметод масиву, повернувшись false. Ви не можете порушити eachметод таким же чином.
Nux
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.