使用流在列表中打印元素

时间:2018-01-20 19:38:46

标签: scala

为什么下面的代码只打印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((), ?)

编写此代码的正确方法是什么?

6 个答案:

答案 0 :(得分:6)

我将我的答案分成两部分:

1。 Scala中的map方法:

您正在使用map,它需要一个没有副作用的功能(打印是副作用)。您正在寻找的是:

l.toStream.foreach(x => print(x))

基本上,一般的想法是map采取某些东西并将其转换为其他东西(例如,增加其值)。而foreach正在对该值执行某些操作,而该值不应该具有返回值。

scala> l.toStream.foreach(x => print(x))
123

2。 Scala中的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 

参考

https://www.coursera.org/learn/progfun2/home/week/2

答案 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)

Scala中的

Streamslazy数据结构,这意味着它们往往只执行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)