【问题标题】:printing elements in list using stream使用流打印列表中的元素
【发布时间】:2018-01-20 19:38:46
【问题描述】:

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

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

【问题讨论】:

  • 我可以通过重复调用tail`scala> val strmx1 = l.toStream.map(x => print(x)) 1strmx1: scala.collection.immutable.Stream[Unit ] = Stream((), ?) scala> val strmx2 = strmx1.tail 2strmx2: scala.collection.immutable.Stream[Unit] = Stream((), ?) scala> val strmx3 = strmx2.tail 3strmx3: scala.collection .immutable.Stream[Unit] = Stream((), ?) scala> val strmx4 = strmx3.tail strmx4: scala.collection.immutable.Stream[Unit] = Stream() `

标签: scala


【解决方案1】:

我的答案分为两部分:

1。 Scala 中的map 方法:

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

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

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

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

2。 Stream 在 Scala 中:

流是lazy,所以 Scala 只计算它需要的值。试试这个:

scala> l.toStream.map(x => x+1)
res2: scala.collection.immutable.Stream[Int] = Stream(2, ?)

你可以看到它计算了第一个值,问号表示它不知道后面是什么,因为它还没有计算它。在您的示例中,第一个值什么都没有,因为 print 不返回任何值。

【讨论】:

    【解决方案2】:

    Stream 是按需数据结构,这意味着在您需要它们之前不会评估所有值。

    例子,

    scala> val stream = (1 to 10000).toStream
    stream: scala.collection.immutable.Stream[Int] = Stream(1, ?)
    

    现在,如果您访问 head 和 tail,流将被评估到第二个索引。

    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

    【讨论】:

      【解决方案3】:

      打印完整的流使用

      l.toStream.print
      

      输出:1, 2, 3, empty

      要打印前 n 个值,您可以使用 take(n)

      l.toStream.take(2).print
      

      打印输出:1, 2, empty

      【讨论】:

        【解决方案4】:

        你可以打印出来

        l.toStream.foreach(println)
        

        但一般而言,尝试打印甚至处理整个Stream 时不小心并不是一个好主意,因为它可能是无限的并且在这样做时会导致错误。

        关于Streamshere的更多信息

        【讨论】:

          【解决方案5】:

          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((), ?)
          

          在这里,您的“转换”函数采用元素 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。但它也在我们的控制台上打印了一些东西,它在它的环境之外。

          这个魔法的一个真实例子是 - 每当你在烤面包机里放一块面包时,都会在绿巨人的当前位置引发一场暴雨。没有人希望绿巨人来找他们的烤面包机。

          【讨论】:

            【解决方案6】:

            总的来说,底线是,您不应该在.map 中使用副作用。当您执行foo.map(bar) 时,它只返回另一个包含元素的集合,该元素是通过将bar 应用于原始集合而生成的。它可能是也可能不是懒惰的。关键是,您应该将任何集合的元素视为未定义,直到有人看到它们为止。

            如果你想要副作用,使用foreach:Seq(1,2,3).toStream.foreach(println)

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2011-10-11
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-07-22
              • 1970-01-01
              • 2020-09-25
              • 1970-01-01
              相关资源
              最近更新 更多