2009-04-19 52 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