2009-04-19 51 views
28

我想从闭包返回,就像在循环中使用break语句一样。如何从常规关闭返回并停止执行?

例如:

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

在上面的if语句,我想停止通过列表迭代,并留下关闭,以避免不必要的迭代。

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

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

回答

24

我想你想用find来代替每个(至少对于指定的例子)。关闭不直接支持中断。

在封面下,groovy实际上并没有使用闭包来寻找,它使用for循环。

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

下面是一个例子:

 
Object.metaClass.eachBreak = { ifClosure, workClosure -> 
    for (Iterator iter = delegate.iterator(); iter.hasNext();) { 
     def value = iter.next() 
     if (ifClosure.call(value)) { 
      workClosure.call(value) 
      break 
     }   
    } 
} 

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

a.eachBreak({ it.startsWith("b") }) { 
    println "working on $it" 
} 

// prints "working on bar" 
2

我认为你正在处理错误的抽象层次。 .each块完全按照它的说法:它为每个元素执行一次闭包。你可能想要的是使用List.indexOf找到合适的specificElement,然后做你需要做的工作。

3

如果要处理所有元素,直到一个特定发现你也可以做这样的事情:

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

虽然你可以用任何使用此种“休息条件”。 我只是用这通过在盖的端返回

counter++ >= n 

以处理集合的第n个元素。

1

正如我所了解的groovy,快捷方式这些类型的循环将抛出用户定义的异常。我不知道语法是什么(而不是grrovy程序员),但在JVM上运行的Groovy所以这将是东西是这样的:

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"; }  
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 

但请记住,“一个不应使用流量控制例外”,具体见这个堆栈溢出问题:Why not use exceptions as regular flow of control?

所以上述方案小于在任何情况下的理想选择。只需使用:

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