首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Scala Streams、memoization和占位符语法

Scala Streams、memoization和占位符语法
EN

Stack Overflow用户
提问于 2015-11-21 07:16:56
回答 2查看 108关注 0票数 1

我最近一直在玩Scala无限流,我注意到了一个奇怪的行为。这个想法是为了证明memoization可以与声明为val的流一起工作。

具有以下测试套件:

代码语言:javascript
复制
import org.scalatest.{Matchers, FunSuite}

class StreamsSuite extends FunSuite with Matchers {
  test("natural numbers stream, proving memoization") {
    var hitCounter = 0
    lazy val Naturals: Stream[Int] = 1 #:: Naturals.map { n =>
      hitCounter += 1
      n + 1
    }

    Naturals.take(3).toIndexedSeq should be(Seq(1, 2, 3))
    hitCounter should be(2)
    Naturals.take(3).toIndexedSeq
    hitCounter should be(2)
    Naturals.take(4).toIndexedSeq
    hitCounter should be(3)
  }
}

一切都如预期般完美地工作着。但是,当我以以下方式更改Stream定义以使用下划线占位符语法时:

代码语言:javascript
复制
lazy val Naturals: Stream[Int] = 1 #:: Naturals.map {
  hitCounter += 1
  _ + 1
}

关于流内容的所有断言仍然有效,但hitCounter将只更新一次(并以值1结束)。

我认为在Scala方面发生了某种优化,一种内联,它抑制了clojure主体中的任何副作用。有谁能解释一下吗?

Scala版本2.11.7

EN

回答 2

Stack Overflow用户

发布于 2015-11-21 08:37:52

以下两个表达式是等效的:

代码语言:javascript
复制
scala> List(1, 2, 3).map { println("foo"); _ + 1 }
foo
res0: List[Int] = List(2, 3, 4)

scala> List(1, 2, 3).map({ println("foo"); _ + 1 })
foo
res1: List[Int] = List(2, 3, 4)

在第二个版本中,您所看到的效果会更清晰一些。map只是一个将函数作为参数的方法,当您给它一个包含多个表达式的块时,它将立即(并且只计算一次)该块,就像它对任何其他表达式一样。

非占位符情况的不同之处在于,箭头之后的任何副作用都发生在函数内部。请看以下两个定义:

代码语言:javascript
复制
scala> val f1: Int => Int = { println("foo"); _ + 1 }
foo
f1: Int => Int = <function1>

scala> val f2: Int => Int = i => { println("foo"); i + 1 }
f2: Int => Int = <function1>

在第一个示例中,括号及其内容是一个计算结果为函数的块,而在第二个示例中,它们是函数的结果块。

票数 6
EN

Stack Overflow用户

发布于 2015-11-22 02:45:01

花括号中使用的占位符语法不是一个函数,而是一个代码块,它以结果的形式返回函数。表达式只计算一次,因此任何副作用都只发生一次。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33837651

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档