为什么下面的代码只打印1而不打印列表元素的其余部分?
scala> val l: List [Int] = List(1,2,3)
l: List[Int] = List(1, 2, 3)
scala> l.toStream.map(x => print(x))
1res3: scala.collection.immutable.Stream[Unit] = Stream((), ?)
编写此代码的正确方法是什么?
答案 0 :(得分:6)
我将我的答案分成两部分:
map
方法:您正在使用map
,它需要一个没有副作用的功能(打印是副作用)。您正在寻找的是:
l.toStream.foreach(x => print(x))
基本上,一般的想法是map
采取某些东西并将其转换为其他东西(例如,增加其值)。而foreach
正在对该值执行某些操作,而该值不应该具有返回值。
scala> l.toStream.foreach(x => print(x))
123
Stream
:Streams是lazy,因此Scala只计算它需要的值。试试这个:
scala> l.toStream.map(x => x+1)
res2: scala.collection.immutable.Stream[Int] = Stream(2, ?)
你可以看到它计算了第一个值,问号表明它不知道它后面是什么,因为它还没有计算它。在你的例子中,第一个值是空的,因为print
没有返回任何值。
答案 1 :(得分:2)
流是按需数据结构,这意味着在您需要之前不会评估所有值。
例如,
scala> val stream = (1 to 10000).toStream
stream: scala.collection.immutable.Stream[Int] = Stream(1, ?)
现在,如果你访问头部和尾部,流将被评估到第二个索引。
scala> stream.head
res13: Int = 1
scala> stream.tail
res14: scala.collection.immutable.Stream[Int] = Stream(2, ?)
scala> stream
res15: scala.collection.immutable.Stream[Int] = Stream(1, 2, ?)
如果您访问索引99,
scala> stream(99)
res16: Int = 100
现在,如果您打印stream
,流将评估到第99个索引,
scala> stream
res17: scala.collection.immutable.Stream[Int] = Stream(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, ?)
最好只处理您需要的流中的那些。您可以使用take()
。
scala> stream.take(50).foreach(x => print(x + " "))
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
所以,回答你的问题可以是,
scala> List(1,2,3).toStream.take(3).foreach(x => print(x + " "))
1 2 3
答案 2 :(得分:1)
打印完整的流使用
l.toStream.print
输出:1, 2, 3, empty
要打印前n个值,您可以使用take(n)
l.toStream.take(2).print
打印输出:1, 2, empty
答案 3 :(得分:0)
您可以使用
进行打印l.toStream.foreach(println)
但一般来说,尝试打印甚至处理并不小心整个Stream
并不是一个好主意,因为它可能是无限的并且在这样做时会导致错误。
有关Streams
here
答案 4 :(得分:0)
Streams
是lazy
数据结构,这意味着它们往往只执行as needed
工作。
scala> val stream1 = Stream.range(1, 10)
// stream1: scala.collection.immutable.Stream[Int] = Stream(1, ?)
在这种情况下,仅计算first
元素。流知道如何计算其余元素,并仅在实际需要时计算它们。例如("消费者"),
scala> val list1 = stream1.toList
// list1: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
scala> stream1.foreach(print)
// 123456789
但是当面对"变形金刚"时,Streams将只保留新的转换,但不适用于整个Stream。 map
方法应该提供转换
scala> val stream2 = stream1.map(i => i + 5)
// stream2: scala.collection.immutable.Stream[Int] = Stream(6, ?)
因此,它只是知道必须将此i => i + 5
函数应用于stream1
的各个元素才能获取stream2
的元素。并且会在需要时(面向任何消费者)这样做。
让我们考虑类似于你的例子,
scala> val stream3 = stream1.map(i => println("element :: " + i))
// element :: 1
// stream3: scala.collection.immutable.Stream[Unit] = Stream((), ?)
在这里你的"转换" function接受一个元素Int
,将其打印出来并在Scala中返回任何名为Unit
或()
的内容。在此输出lazy
流,将为first
元素计算此变换,并且不会为休息而做。此处的计算将导致element :: 1
被打印。
现在,让我们看看当我们应用一些消费者时会发生什么,
scala> val list3 = stream3.toList
// element :: 2
// element :: 3
// element :: 4
// element :: 5
// element :: 6
// element :: 7
// element :: 8
// element :: 9
// list3: List[Unit] = List((), (), (), (), (), (), (), (), ())
大多数人看起来都不对。所有我想将我的流转换为列表,但为什么所有这些行都被打印出来。
这就是为什么当您使用map
时,您应该提供pure
功能。
什么是纯粹的功能?简单的答案是纯函数只能完成它应该做的事情而不做其他事情。它不会导致其范围之外的任何变化。它只需要一些东西并回馈其他东西。
以下所有内容都是纯函数,
scala> val pf1 = (i: Int) => i + 1
// pf1: Int => Int = $$Lambda$1485/1538411140@6fdc53db
scala> val pf2 = (i: Int) => {
| val x = 100
| val xy = 200
| xy + i
| }
// pf2: Int => Int = $$Lambda$1487/14070792@7bf770ba
scala> val pf3 = (i: Int) => ()
// pf3: Int => Unit = $$Lambda$1486/1145379385@336cd7d5
如果以下不是纯函数,
val npf1 = (i: Int) => println(i)
// npf1: Int => Unit = $$Lambda$1488/1736134005@7ac97ba6
因为它会导致一个神奇的"改变环境。
为什么"魔法"?因为,它声称是类型Int => Unit
的函数,这意味着它应该只是将Int转换为Unit
。但它也在我们的控制台上打印了一些东西,它不在环境中。
这种神奇的真实例子就是 - 每当你在烤面包机里放一块面包时,就会在绿巨人的当前位置引起暴风雨。没有人希望绿巨人来找他们的烤面包机。
答案 5 :(得分:0)
一般来说,最重要的是,你不应该在.map
中使用副作用。当你执行foo.map(bar)
只返回另一个集合时,它包含通过将bar
应用于原始集合而生成的元素。它可能是也可能不是懒惰的。关键是,您应该将任何集合的元素视为未定义,直到看到它们为止。
如果您想要副作用,请使用foreach:Seq(1,2,3).toStream.foreach(println)