在Groovy闭包中模拟“继续”的最佳模式

时间:2008-10-15 17:32:56

标签: groovy closures

似乎Groovy不支持封闭内的breakcontinue。模拟这个的最佳方法是什么?

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

6 个答案:

答案 0 :(得分:71)

你只能支持干净,而不是休息。特别是像每条线和每条线的东西。无法支持中断与如何评估这些方法有关,没有考虑不完成可以传递给方法的循环。以下是如何支持继续 -

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

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

如果您的样本真的那么简单,那么这对于可读性是有好处的。

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

另一个选项,模拟继续在字节码级别通常会做什么。

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。

答案 1 :(得分:15)

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

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

}

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

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

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

答案 2 :(得分:10)

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

这可用于模拟休息:

final static BREAK = new Exception();
//...
try {
  ... { throw BREAK; }
} catch (Exception ex) { /* ignored */ }

答案 3 :(得分:8)

使用返回 继续任何关闭 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

答案 4 :(得分:3)

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

答案 5 :(得分:1)

使用rx-java,您可以将可迭代变换为可观察的。

然后你可以用过滤器替换 continue ,用 takeWhile 替换 break

以下是一个例子:

import rx.Observable

Observable.from(1..100000000000000000)
          .filter { it % 2 != 1} 
          .takeWhile { it<10 } 
          .forEach {println it}