首页 文章

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

提问于
浏览
66

似乎Groovy不支持闭包中的 breakcontinue . 模拟这个的最佳方法是什么?

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

6 回答

  • 15

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

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

    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 .

  • 8

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

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

    中断支持不会在闭包级别发生,而是由接受闭包的方法调用的语义隐含 . 简而言之,这意味着不是在像集合那样处理整个集合的东西上调用“each”,而应该调用find,它将处理直到满足某个条件 . 大多数(全部?)次你觉得需要从闭包中断开你真正想要做的是在迭代期间找到一个特定的条件,这使得find方法不仅符合你的逻辑需求而且符合你的意图 . 遗憾的是,一些API缺乏对find方法的支持......例如File . 有可能所有的时间花在争论语言中应该包括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}/ }
    

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

  • 3

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

    这可以用来模拟休息:

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

    使用 return 继续并关闭 any .

    Example

    文件内容:

    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
    
  • 71

    在这种情况下,您应该想到 find() 方法 . 它在第一次关闭传递给它后停止返回true .

  • 10

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

    然后你可以用过滤器替换继续并使用takeWhile中断

    这是一个例子:

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

相关问题