您能摆脱Groovy的“每个”封闭吗?


143

是否有可能break从一个Groovy .each{Closure},或者我应该用一个经典的循环呢?

Answers:


194

不,您不能在不引发异常的情况下中止“每个”。如果希望中断在特定条件下中止,则可能需要经典循环。

另外,您可以使用“查找”闭包代替每个闭包,并在需要休息时返回true。

此示例在处理整个列表之前将中止:

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
Ted Naleid 2010年

2
我已经尝试过这种情况:def a = [1,2,3,4,5,6,7,-1,-2]它返回1 2 3 4 5 -1 -2。因此,“ BreaK”不起作用。
Phat H. VU 2013年

正确的发现是正确的选择,每个都永远不会因返回而中断
Pushkar'July

find不是更好any-请参阅下面的其他答案从@Michal一个对我的作品
大黄

Ted Naleid对常规的修补程序非常有用。如果您实际上需要查找操作的结果而不是元素本身,请使用findResult。例如:​def test = [2] test.findResult{ it * 2 }​返回4而不是2
Doug

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

只是个把戏。如果在“ break”之后有一个声明,将会发生什么。遇到“中断”之后,该语句仍将被执行。
Phat H. VU 2013年

2
@Phat H. VU我添加了返回。“破发”后的声明将不会被执行
米哈尔ž穆达

1
感谢您的回复。你是对的。我也支持您的回答。更多:any方法在DefaultGroovyMethods中定义,并且它是一个谓词函数,如果集合中的元素满足提供的谓词闭包,则返回true。
Phat H. VU 2013年

使用any()这种方式是有点误导,但它肯定不会工作,让您的能力突破继续
vegemite4me,2014年

1
就像@ vegemite4me一样,我认为这是一个“技巧”,但您对它的含义不太了解。为了可维护性,您不应使用此解决方案。
MatRt 2015年

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的find,grep,collect,inject等方法之一。他们通常会进行一些“配置”,然后“知道”如何为您执行迭代,以便您实际上可以尽可能避免命令式循环。


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
如果您有10亿行,并且内部闭包在第一次调用时返回true,那么您将迭代超过10亿减去一个值。:(
sbglasius

-4

(1..10)。每个{

如果(<5)

打印它

其他

返回假


2
这不会打破each,它只是不会打印大于4的值。else是多余的,如果没有它,您的代码将执行相同的操作。另外,如果您放置在之前和之后,则可以证明each不中断。return falseprintln "not breaking"elsereturn false
Stefan van den Akker's

请至少花最少的精力来格式化代码,以提高可读性。当您回复时会有一个视觉预览,并提供了许多说明如何进行操作
Mateusz是

-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
OP确实要问这,尽管显然不是他的意思。
2013年

我可以描述一下为什么这票数不佳。在我看来,这与最高答辩率(+133票)所说的概念相同。
Skepi '17

1
@Skepi您不能“打破”以上关闭。您可以any通过返回中断数组的方法false。您不能each以相同的方式破坏方法。
Nux
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.