你能从 Groovy 的“ each”结束中解脱出来吗?

是否可以从 Groovy .each{Closure}转换为 break,或者我应该使用经典的循环?

155036 次浏览

不行,你不能在不抛出异常的情况下终止“ each”。如果希望中断在特定条件下中止,那么可能需要一个经典循环。

或者,您可以使用“ 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。

使用接受闭包的自定义 break 行为编写自己的迭代器方法也非常容易:

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

不,在 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、注入等方法之一。他们通常采用一些“配置”,然后“知道”如何为您执行迭代,这样您就可以在任何可能的地方实际避免命令式循环。

你可以用 RETURN打破。例如

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

我喜欢!

任何闭包替换 每个人循环。

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

只是使用特殊封闭

// 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
}

(1. . 10)

如(少于5)

打印出来

别的

返回假

您不能从 Groovy each 循环中断,但是您可以从 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

这就是我们想要的!