69

似乎 Groovy 不支持breakcontinuefrom 闭包。模拟这种情况的最佳方法是什么?

revs.eachLine { line -> 
    if (line ==~ /-{28}/) {
         // continue to next line...
    }
}
4

6 回答 6

72

你只能支持干净地继续,不能打破。尤其是像 eachLine 和 each 这样的东西。无法支持中断与如何评估这些方法有关,没有考虑没有完成可以传达给方法的循环。以下是如何支持继续 -

最佳方法(假设您不需要结果值)。

revs.eachLine { line -> 
    if (line ==~ /-{28}/) {
        return // returns from the closure
    }
}

如果您的示例真的那么简单,那么这对可读性很有好处。

revs.eachLine { line -> 
    if (!(line ==~ /-{28}/)) {
        // do what you would normally do
    }
}

另一种选择,模拟 continue 通常在字节码级别执行的操作。

revs.eachLine { line -> 
    while (true) {
        if (line ==~ /-{28}/) {
            break
        }
        // rest of normal code
        break
    }

}

支持中断的一种可能方法是通过异常:

try {
    revs.eachLine { line -> 
        if (line ==~ /-{28}/) {
            throw new Exception("Break")
        }
    }
} catch (Exception e) { } // just drop the exception

您可能希望使用自定义异常类型来避免屏蔽其他真正的异常,尤其是当您在该类中进行其他可能引发真正异常的处理时,例如 NumberFormatExceptions 或 IOExceptions。

于 2008-10-15T18:00:29.003 回答
17

闭包不能中断或继续,因为它们不是循环/迭代构造。相反,它们是用于处理/解释/处理迭代逻辑的工具。您可以通过简单地从闭包返回而不进行处理来忽略给定的迭代,如下所示:

revs.eachLine { line -> 
    if (line ==~ /-{28}/) {
            return
    }

}

中断支持不会发生在闭包级别,而是由接受闭包的方法调用的语义所暗示。简而言之,这意味着不要在诸如旨在处理整个集合的集合之类的东西上调用“每个”,而应该调用 find 它将一直处理直到满足特定条件。大多数(全部?)时间你觉得需要打破闭包,你真正想做的是在你的迭代中找到一个特定的条件,这使得 find 方法不仅符合你的逻辑需求,而且符合你的意图。遗憾的是,一些 API 缺乏对 find 方法的支持……例如 File。可能所有花费在争论语言是否应该包括中断/继续的时间都可以很好地用于将 find 方法添加到这些被忽视的区域。像 firstDirMatching(Closure c) 或 findLineMatching(Closure c) 这样的东西会走很长的路,回答 99+% 的“为什么我不能摆脱......?” 邮件列表中弹出的问题。也就是说,通过 MetaClass 或 Categories 自己添加这些方法很简单。

class FileSupport {
   public static String findLineMatching(File f, Closure c) {
      f.withInputStream {
         def r = new BufferedReader(new InputStreamReader(it))
         for(def l = r.readLine(); null!=l; l = r.readLine())
             if(c.call(l)) return l
         return null
      }
   }
}

using(FileSupport) { new File("/home/me/some.txt").findLineMatching { line ==~ /-{28}/ }

其他涉及异常和其他魔法的 hack 可能会起作用,但在某些情况下会引入额外的开销,并在其他情况下会混淆可读性。真正的答案是查看您的代码并询问您是否真正在迭代或搜索。

于 2008-10-18T02:43:04.360 回答
11

如果您在 Java 中预先创建一个静态 Exception 对象,然后从闭包内抛出(静态)异常,则运行时成本是最小的。真正的成本是在创建异常时产生的,而不是在抛出它时。根据 Martin Odersky(Scala 的发明者)的说法,许多 JVM 实际上可以将 throw 指令优化为单次跳转。

这可以用来模拟休息:

final static BREAK = new Exception();
//...
try {
  ... { throw BREAK; }
} catch (Exception ex) { /* ignored */ }
于 2010-05-24T15:01:53.423 回答
10

使用return继续,使用任何关闭break

例子

文件内容:

1
2
----------------------------
3
4
5

Groovy 代码:

new FileReader('myfile.txt').any { line ->
    if (line =~ /-+/)
        return // continue

    println line

    if (line == "3")
        true // break
}

输出:

1
2
3
于 2013-10-16T21:05:07.077 回答
4

在这种情况下,您可能应该考虑该find()方法。它在第一次传递给它的闭包返回 true 后停止。

于 2012-03-27T01:59:06.077 回答
2

使用rx-java,您可以将一个可迭代对象转换为可观察对象。

然后你可以用过滤器替换continue并用takeWhile中断

这是一个例子:

import rx.Observable

Observable.from(1..100000000000000000)
          .filter { it % 2 != 1} 
          .takeWhile { it<10 } 
          .forEach {println it}
于 2015-06-02T09:39:41.297 回答