Groovy中链式函数方法的惰性求值

ton*_*edz 9 groovy closures functional-programming lazy-evaluation java-8

我在Java中看到的

Java 8允许对链式函数进行惰性求值,以避免性能损失.

例如,我可以有一个值列表并像这样处理它:

someList.stream()
        .filter( v -> v > 0)
        .map( v -> v * 4)
        .filter( v -> v < 100)
        .findFirst();
Run Code Online (Sandbox Code Playgroud)

我将一些闭包传递给在流上调用的方法来处理集合中的值,然后只抓取第一个.

这看起来好像代码必须迭代整个集合,过滤它,然后迭代整个结果并应用一些逻辑,然后再次过滤整个结果,最后只抓取一个元素.

实际上,编译器以更智能的方式处理这个问题并优化所需的迭代次数.

这是可能的,因为findFirst在调用之前不会进行实际处理.这样编译器就知道我想要实现什么,并且可以找到如何以有效的方式完成它.

请看Venkat Subramaniam的演示视频,以获得更长的解释.

我想在Groovy中做些什么

在StackOverflow上回答有关Groovy问题时,我想出了一种方法来执行OP试图以更易读的方式实现的任务.我没有提出建议,因为这意味着性能下降.

这是一个例子:

collectionOfSomeStrings.inject([]) { list, conf ->  if (conf.contains('homepage')) { list } else { list << conf.trim() } }
Run Code Online (Sandbox Code Playgroud)

从语义上讲,这可以改写为

collectionOfSomeStrings.grep{ !it.contains('homepage')}.collect{ it.trim() }
Run Code Online (Sandbox Code Playgroud)

我发现它更容易理解,但可读性是有代价的.此代码需要传递原始集合,并对结果进行另一次迭代grep.这不太理想.

它看起来不像GDK grep,collect并且findAll像Java 8的流API中的方法一样懒惰地评估方法.有没有办法让他们表现得像这样?我可以使用Groovy中的替代库吗?

我想可能在Groovy中以某种方式使用Java 8并具有此功能.我欢迎对细节的解释,但理想情况下,我希望能够使用旧版本的Java来做到这一点.

我找到了一种结合闭包方法,但它并不是我想做的事情.我不仅要关闭自己的闭包,还要传递它传递给它的函数.

谷歌搜索Groovy和Streams主要产生与I/O相关的结果.通过搜索惰性求值,函数和Groovy,我没有找到任何有趣的东西.

dma*_*tro 8

以cfrick的评论为例,添加建议作为答案:

@Grab( 'com.bloidonia:groovy-stream:0.8.1' )
import groovy.stream.Stream

List integers = [ -1, 1, 2, 3, 4 ]

//.first() or .last() whatever is needed
Stream.from integers filter{ it > 0 } map{ it * 4 } filter{ it < 15 }.collect()
Run Code Online (Sandbox Code Playgroud)

蒂姆,我仍然知道你几年前做过什么.;-)

  • PS:前一段时间有关于最新版本的博客http://blog.bloidonia.com/post/79285969149/groovy-stream-updates-and-java-integration (3认同)

cfr*_*ick 5

Groovy 2.3支持jdk8 groovy.codehaus.org/Groovy+2.3+release+notes.你的例子使用groovy闭包工作正常:

[-1,1,2,3,4].stream().filter{it>0}.map{it*4}.filter{it < 100}.findFirst().get()
Run Code Online (Sandbox Code Playgroud)

如果你不能使用jdk8,你可以按照其他答案的建议或使用RxJava/RxGroovy实现"相同" :

@Grab('com.netflix.rxjava:rxjava-groovy:0.20.7')
import rx.Observable
Observable.from( [-1, 1, 2, 3, 4, 666] )
    .filter { println "f1 $it"; it > 0 }
    .map { println "m1 $it"; it * 4 }
    .filter { println "f2 $it"; it < 100 }
    .subscribe { println "result $it" }
Run Code Online (Sandbox Code Playgroud)