【问题标题】:Lazy evaluation of chained functional methods in GroovyGroovy 中链式函数方法的惰性评估
【发布时间】:2015-01-17 10:05:23
【问题描述】:

我在 Java 中看到的

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

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

someList.stream()
        .filter( v -> v > 0)
        .map( v -> v * 4)
        .filter( v -> v < 100)
        .findFirst();

我将许多闭包传递给在流上调用的方法以处理集合中的值,然后只获取第一个。

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

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

这是可能的,因为在调用findFirst 之前不会进行任何实际处理。这样编译器就知道我想要实现什么,它可以找出如何以一种有效的方式来实现它。

查看video of a presentation by Venkat Subramaniam 以获得更详细的说明。

我想在 Groovy 中做什么

在回答question about Groovy here on StackOverflow 时,我想出了一种方法来执行 OP 试图以更易读的方式完成的任务。我没有建议它,因为这意味着性能下降。

示例如下:

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

在语义上,这可以重写为

collectionOfSomeStrings.grep{ !it.contains('homepage')}.collect{ it.trim() }

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

它看起来不像 GDK 的 grepcollectfindAll 方法像 Java 8 的流 API 中的方法那样被延迟评估。有没有办法让他们表现得像这样?我可以使用 Groovy 中的任何替代库吗?

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

我找到了way to combine closures,但这并不是我真正想做的。我不仅想链接闭包本身,还想链接我传递给它们的函数。

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

【问题讨论】:

  • groovy 2.3 支持 jdk8 groovy.codehaus.org/Groovy+2.3+release+notes。您的示例使用 groovy 闭包可以正常工作:[-1,1,2,3,4].stream().filter{it&gt;0}.map{it*4}.filter{it &lt; 100}.findFirst().get()
  • @cfrick 我可以使用早期版本的 JDK 获得这样的结果吗?
  • 没有。这实际上使用了 JDK8 API,但可以使用闭包代替 lambdas
  • 当然,让我们看看其他人怎么说。也许像 rxgroovy 这样的东西可以为你工作?

标签: groovy functional-programming closures java-8 lazy-evaluation


【解决方案1】:

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()

如果您不能使用 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" }

【讨论】:

    【解决方案2】:

    以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()
    

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

    【讨论】:

    • Stream.from integers filter{ println "filter1 $it"; it &gt; 0 } map{ println "map $it"; it * 4 } filter{println "filter2 $it"; it &lt; 15 }.collect() 从输出来看,这是我想要的。酷!
    • PS:前段时间有一篇关于最新版本的博客blog.bloidonia.com/post/79285969149/…
    • @tim_yates 感谢您的链接,尤其是图书馆。迫不及待想在工作中使用它。
    猜你喜欢
    • 1970-01-01
    • 2014-02-08
    • 1970-01-01
    • 1970-01-01
    • 2015-05-29
    • 1970-01-01
    • 2013-03-11
    • 2017-01-18
    相关资源
    最近更新 更多