我最近一直在玩Scala无限Streams,我注意到了一个奇怪的行为。这个想法是为了证明memoization适用于声明为@Override
public void onViewCreated (View view, Bundle savedInstanceState){
ButterKnife.bind(this, view);
recyclerFab.hide(false);
tabPullRefresh.setRefreshStyle(PullRefreshLayout.STYLE_MATERIAL);
...
的Streams。
拥有以下测试套件:
val
所有功能都完美且符合预期。但是,当我将Stream定义更改为以下列方式使用下划线占位符语法时:
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内容的所有断言仍然有效,但lazy val Naturals: Stream[Int] = 1 #:: Naturals.map {
hitCounter += 1
_ + 1
}
只会更新一次(并以值1结束)。
我认为Scala方面存在某种优化,一种内联,它可以抑制clojure体内的任何副作用。任何人都可以解释?
Scala版本2.11.7
答案 0 :(得分:6)
以下两个表达式是等效的:
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
只是一个将函数作为参数的方法,当你给它一个包含多个表达式的块时,它会立即(并且只有一次)计算块,就像任何其他表达式一样。
非占位符案例的不同之处在于箭头后的任何副作用都发生在函数内部。采用以下两个定义:
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>
在第一个中,括号及其内容是一个评估函数的块,而在第二个中它们是一个作为函数结果的块。
答案 1 :(得分:0)
大括号内使用的占位符语法不是函数,而是将函数作为结果返回的代码块。表达式被评估一次,因此任何副作用只发生一次。