【发布时间】:2011-03-04 05:58:57
【问题描述】:
是否可以从 Groovy 的 .each{Closure} break,或者我应该使用经典循环来代替?
【问题讨论】:
标签: groovy
是否可以从 Groovy 的 .each{Closure} break,或者我应该使用经典循环来代替?
【问题讨论】:
标签: groovy
不,您不能在不引发异常的情况下中止“每个”。如果您希望中断在特定条件下中止,您可能需要一个经典循环。
或者,您可以使用“find”闭包而不是 each 并在您需要中断时返回 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
【讨论】:
find 比 any 好 - 请参阅下面来自 @Michal 的另一个答案,一个对我有用
def test = [2] test.findResult{ it * 2 } 将返回 4 而不是 2
用 any 闭包替换 each 循环。
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
【讨论】:
any() 有点误导,但它确实有效,并让您能够break 或continue。
不,你不能在不抛出异常的情况下从 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 等方法之一。他们通常会进行一些“配置”,然后“知道”如何为您进行迭代,以便您实际上可以尽可能避免命令式循环。
【讨论】:
只使用特殊的闭包
// 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
}
【讨论】:
你不能从 Groovy 的每个循环中中断,但你可以从 java “增强”的 for 循环中中断。
def a = [1, 2, 3, 4, 5, 6, 7]
for (def i : a) {
if (i < 2)
continue
if (i > 5)
break
println i
}
输出:
2
3
4
5
这可能并不完全适合所有情况,但对我有帮助:)
【讨论】:
我同意其他答案,不要使用异常来破坏每个。我也不喜欢创建一个额外的闭包eachWithBreak,而不是这个我更喜欢一种现代的方法:让我们使用each 来迭代集合,根据要求,但优化集合以仅包含那些要迭代的元素,例如findAll:
collection.findAll { !endCondition }.each { doSomething() }
例如,如果我们要打破counter == 3时我们可以编写这段代码(已经建议):
(0..5)
.findAll { it < 3 }
.each { println it }
这将输出
0
1
2
到目前为止一切都很好,但是您会注意到一个小的差异。我们的结束条件,否定counter == 3 并不完全正确,因为!(counter==3) 不等于it < 3。这是使代码正常工作所必需的,因为findAll 实际上并没有中断循环,而是一直持续到结束。
为了模拟真实情况,假设我们有以下代码:
for (n in 0..5) {
if (n == 3)
break
println n
}
但是我们要使用each,所以让我们用一个函数来重写它来模拟中断条件:
def breakWhen(nr) { nr == 3 }
(0..5)
.findAll { !breakWhen(it) }
.each { println it }
输出:
0
1
2
4
5
现在您看到了findAll 的问题。这不会停止,但会忽略不满足条件的元素。
为了解决这个问题,我们需要一个额外的变量来记住中断条件何时变为真。此后,findAll 必须忽略所有剩余元素。
它应该是这样的:
def breakWhen(nr) { nr == 3 }
def loop = true
(0..5)
.findAll {
if (breakWhen(it))
loop = false
!breakWhen(it) && loop
} .each {
println it
}
输出:
0
1
2
这就是我们想要的!
【讨论】:
(1..10).each{
如果(它
打印出来
其他
返回错误
【讨论】:
each,它根本不会打印大于 4 的值。else 是多余的,如果没有它,您的代码也会这样做。此外,如果您将println "not breaking" 放在else 之后和return false 之前,您可以证明each 不会与return false 中断。
您可以通过RETURN 休息。例如
def a = [1, 2, 3, 4, 5, 6, 7]
def ret = 0
a.each {def n ->
if (n > 5) {
ret = n
return ret
}
}
它对我有用!
【讨论】:
false 来破坏数组的any 方法。你不能以同样的方式破坏each 方法。