我在玩Scala的流,我不确定我是否理解这个想法。让我们考虑以下代码
def fun(s: Stream[Int]): Stream[Int] = Stream.cons(s.head, fun(s.tail))
执行这个
val f = fun(Stream.from(7))
f take 14 foreach println
结果
7 8 9 10 ... up to 20
假设我理解这一点。
现在,稍微更改代码(头上加2)
def fun(s: Stream[Int]): Stream[Int] = Stream.cons(s.head + 2, fun(s.tail))
中的结果
9 10 11 ... up to 22
我想我又明白了。问题从下一个例子开始(d
def fun(s: Stream[Int]): Stream[Int] = Stream.cons(s.head / 2, fun(s.tail))
3 4 4 5 5 6 6 7 7 8 8 9 9 10
我不明白,请解释为什么会这样?类似地,减法也没有像我预期的那样表现
def fun(s: Stream[Int]): Stream[Int] = Stream.cons(s.head - 2, fun(s.tail))
输出
5 6 7 8 9 10 ... up to 18
给定您的"take":7 8 9 10 ... up to 20
,
-
当您在每个元素上
+ 2
时会发生什么? -
当你在每个元素上
/ 2
(int
算术)时会发生什么? -
在每个元素上
- 2
时会发生什么?
如果您将其视为映射Stream
,会更直观吗?
scala> val s1 = Stream.from(10)
s1: scala.collection.immutable.Stream[Int] = Stream(10, ?)
scala> val s2 = s1 map (_ * 2)
s2: scala.collection.immutable.Stream[Int] = Stream(20, ?)
scala> s2.take(5).toList
res0: List[Int] = List(20, 22, 24, 26, 28)
scala> val s3 = s1 map (_ / 2)
s3: scala.collection.immutable.Stream[Int] = Stream(5, ?)
scala> s3.take(5).toList
res1: List[Int] = List(5, 5, 6, 6, 7)
scala> val s4 = s1 map (_ - 2)
s4: scala.collection.immutable.Stream[Int] = Stream(8, ?)
scala> s4.take(5).toList
res2: List[Int] = List(8, 9, 10, 11, 12)
好的,让我们试着把它分解。。。
def fun(s: Stream[Int]): Stream[Int] = Stream.cons(s.head, fun(s.tail))
是一个函数,它取一个Stream
,将其head
和tail
分离,递归地应用于tail
,然后用cons
运算符重新组合这两个结果。
由于在该操作期间head
没有被触摸,因此Stream
被逐个地重建为以前的样子。
val f = fun(Stream.from(7))
f
它与Stream.from(7)
相同[即从7开始的递增整数的无限序列]
打印f take 14
实际上表明,我们有从7开始的前14个数字[即7,8,9,…,20]
接下来发生的是,当用cons
重建流时,每个元素都会以某种方式修改
def fun(s: Stream[Int]): Stream[Int] = Stream.cons(s.head + 2, fun(s.tail))
这在将head
与修饰的tail
重组之前将其添加2。后者以相同的方式进行修改,其第一个元素被添加到2,然后重新组合到其自己的tail
,因此也是自己的。
如果我们再次假设s
包含从7开始的数字,则发生的情况看起来像
fun(s) = cons(7 + 2, cons(8 + 2, cons(9 + 2, ... ad infinitum ... )))))
这与向流CCD_ 22的每个元素添加2相同。
代码通过打印"9到22"来确认这一点,确切地说是"7到20",每个元素都添加了2。
其他例子类似:
- 每个元素的流除以2(并四舍五入到地板整数值,因为
Stream
是用Int
值键入的) - 每个元素递减2的流