36

我想从闭包中返回,就像在循环中使用 break 语句一样。

例如:

largeListOfElements.each{ element->
    if(element == specificElement){
        // do some work          
        return // but this will only leave this iteration and start the next 
    }
}

在上面的 if 语句中,我想停止遍历列表并离开闭包以避免不必要的迭代。

我见过一个解决方案,其中在闭包内引发异常并在外部捕获,但我不太喜欢该解决方案。

除了更改代码以避免这种算法之外,是否有任何解决方案?

4

7 回答 7

27

我认为您想使用 find 而不是 each (至少对于指定的示例)。闭包不直接支持中断。

在幕后,groovy 实际上并没有为 find 使用闭包,而是使用了 for 循环。

或者,您可以编写自己的增强版本的 find/each 迭代器,该版本接受条件测试闭包,如果找到匹配项则调用另一个闭包,如果匹配项则中断。

这是一个例子:

Object.metaClass.eachBreak = { ifClosure, workClosure ->
    for (Iterator iter = delegate.iterator(); iter.hasNext();) {
        def 值 = iter.next()
        如果(ifClosure.call(值)){
            workClosure.call(值)
            休息
        }        
    }
}

def a = ["foo", "bar", "baz", "qux"]

a.eachBreak( { it.startsWith("b") } ) {
    println "正在处理 $it"
}

// 打印“工作条”
于 2009-04-20T00:35:46.013 回答
5

我认为您正在处理错误的抽象级别。该.each块完全按照它所说的那样做:它为每个元素执行一次闭包。相反,您可能想要的是用来List.indexOf找到正确的specificElement,然后在上面做您需要做的工作。

于 2009-04-19T15:55:10.867 回答
3

如果您想处理所有元素,直到找到特定元素,您还可以执行以下操作:

largeListOfElements.find { element ->
    // do some work
    element == specificElement
}

尽管您可以将其与任何类型的“中断条件”一起使用。我只是用它来处理集合的前 n 个元素,方法是返回

counter++ >= n

在关闭结束时。

于 2009-12-13T14:10:54.450 回答
1

据我了解,简化此类循环的方法是抛出用户定义的异常。我不知道语法是什么(不是一个 groovy 程序员),但是 groovy 在 JVM 上运行,所以它会是这样的:

class ThisOne extends Exception {Object foo; ThisOne(Object foo) {this.foo=foo;}}

try { x.each{ if(it.isOk()) throw new ThisOne(it); false} }
catch(ThisOne x) { print x.foo + " is ok"; }     
于 2010-08-13T02:49:54.790 回答
1

在 paulmurray 的回答之后,我不确定自己从闭包中抛出的异常会发生什么,所以我编写了一个易于思考的 JUnit 测试用例:

class TestCaseForThrowingExceptionFromInsideClosure {

    @Test
    void testEearlyReturnViaException() {
        try {
            [ 'a', 'b', 'c', 'd' ].each {                 
                System.out.println(it)
                if (it == 'c') {
                    throw new Exception("Found c")
                } 
            }
        }
        catch (Exception exe) {
            System.out.println(exe.message)
        }
    }
}  

上面的输出是:

a
b
c
Found c

但请记住,“不应使用异常进行流控制”,请特别参阅这个 Stack Overflow 问题:为什么不使用异常作为常规控制流?

因此,上述解决方案在任何情况下都不太理想。只需使用:

class TestCaseForThrowingExceptionFromInsideClosure {

    @Test
    void testEarlyReturnViaFind() {
        def curSolution
        [ 'a', 'b', 'c', 'd' ].find {                 
            System.out.println(it)
            curSolution = it
            return (it == 'c') // if true is returned, find() stops
        }
        System.out.println("Found ${curSolution}")
    }
}  

上面的输出也是:

a
b
c
Found c
于 2013-02-15T22:34:59.363 回答
0

今天我在处理每个闭包时都遇到了类似的问题。我想根据我的情况打破执行流程,但做不到。

在 groovy 中最简单的方法是在列表上使用any()而不是 each() 如果您希望基于某些条件返回布尔值。

于 2019-03-29T14:07:43.497 回答
0

好的 olefor循环在 Groovy 中仍然适用于您的用例

for (element in largeListOfElements) {
    if(element == specificElement){
        // do some work          
        return
    }
}
于 2021-01-13T11:01:52.890 回答