【问题标题】:Immutable Lists in ScalaScala 中的不可变列表
【发布时间】:2015-12-14 14:15:20
【问题描述】:

我只是想弄清楚像 List 这样不可变的东西是如何工作的,以及如何向其中添加东西?

我很抱歉问了这么愚蠢的问题,但是为什么我的列表打印出来时总是空的?

var end = false
val list = List()
while (!end) {
  val input = scala.io.StdIn.readLine("input:")
  if (input == "stop" ) end = true
  else input :: list
}

println(list)

}

抱歉给我带来的不便和这个相当愚蠢的问题!

【问题讨论】:

标签: list scala add immutability


【解决方案1】:

我只是想弄清楚像 List 这样不可变的东西是如何工作的,以及如何向其中添加东西?

你不能。毕竟,这就是不可变的意思。如果拉丁语不是你的一杯茶,immutable 的英文翻译是 unchangeable。现在应该清楚了,为什么你不能改变一些不可改变的

我很抱歉问了这么愚蠢的问题,但是为什么我的列表打印出来时总是空的?

您创建了一个空列表,并且您永远不会更改它(因为它无法无论如何都无法更改)。所以,当然是空的。

可以做什么,但是,创建一个 new 列表,它几乎与 old 列表完全一样,除了新的前面的项目。这就是你在这里所做的:

input :: list

但是,您不会将这个新列表分配到任何地方,也不会返回它,而是完全忽略它。

如果您想以任何方式实际使用您的列表,您需要以某种方式记住它。最明显的解决方案是将其分配给一个变量:

var end = false
var list: List[String] = List() // note: `var` instead of `val`
while (!end) {
  val input = scala.io.StdIn.readLine("input:")
  if (input == "stop" ) end = true
  else list = input :: list // note: assign to `list`
}
println(list)

但是,这不是很地道。毕竟,我们现在已经获取了一个 immutable 列表并将其分配给一个 mutable 变量…… IOW,我们刚刚移动了可变性。

相反,我们可以使用递归解决方案:

def buildListFromInput(list: List[String] = List()): List[String] = {
  val input = scala.io.StdIn.readLine("input:")
  if (input == "stop") list else buildListFromInput(input :: list)
}

println(buildListFromInput())

这个方案不仅是递归的,递归调用也是在tail位置(IOW,方法是tail-recursive),也就是说它会只是与while 循环一样高效(实际上,它将被编译为while 循环,或者更准确地说,编译为GOTO)。 Scala 语言规范保证 Scala 的所有实现必须消除直接尾递归。

【讨论】:

  • 非常感谢!是的,我的问题是我无法找到一个解决方案,将这个输入添加到列表中,并且在第一个循环之后没有停下来请求输入。当我尝试使用函数时,它起作用了,因为我得到了一个返回值,但是在尝试循环输入时我完全迷失了!
【解决方案2】:

原因

println(list)

只是打印出一个空列表是因为位

input :: list

实际上并没有改变列表本身。在这种情况下,只是临时创建一个包含最前面的输入的列表。

试试

println(input :: list) 

val newList = input :: list
println(newList)

你就会明白我的意思了。

【讨论】:

    【解决方案3】:

    尝试以更实用的方式重写代码。对不可变数据结构的每个操作都会返回带有变化的新实例。所以:: 操作员在前面创建了带有input 的新列表。您可能想尝试将此代码重写为尾递归函数,如下所示。

    @tailrec
    def scanInput(continue: Boolean,acc: List[String]): List[String] = {
      val input = scala.io.StdIn.readLine("input:")
      if(!continue) acc
      else scanInput(input != "stop", input :: acc)
    }
    

    以上代码没有变异状态,更适合Scala函数式风格。

    【讨论】:

    • 非常感谢您,先生,您的帮助!正如我所看到的,递归在函数式编程中似乎非常重要,所以我将看看它是如何工作的!
    【解决方案4】:

    在 scala 中列表是不可变的。

    那我怎样才能将项目添加到列表中呢?

    当您将项目添加到列表时,新的List 实例会以项目作为其头部,并且它的尾部现在包含先前的列表。

    如果您在内部有名为 intList 的“1,2,3”列表,则表示为

    List(3, List(2, List(1, Nil) ) )

    如果你向这个intList添加一个元素4

    列表(4,intList)

    让我们称之为newList

    注意intList仍包含List(3, List(2, List(1, Nil) ) )

    如果您希望 intList 引用 newList 您将不得不这样做

    intList = intList.add(4)
    


    如何修复我的代码

    将列表从 val 更改为 var。然后您可以将结果列表分配给list 变量

    list = input :: list
    


    来源:关于 Scala 的在线课程,名为 Functional Programming Principles in Scala

    【讨论】:

    • 您还必须将 list 设为 var 才能使此分配起作用。
    【解决方案5】:

    感谢大家的帮助,非常感谢大家的帮助! 我应该仔细研究一下递归,因为它似乎真的很重要,就像在 Scala 中一样! 但是通过您的帮助,我对它的工作原理有了更好的了解!

    我只是想弄清楚您的解决方案是如何工作的,并创建了我自己的解决方案:

    val list = List()
    
    def scanInput(acc: List[String]): List[String] = {
    val input = scala.io.StdIn.readLine("input:")
    input match {
    case "stop" => acc
    case _ => scanInput(input :: acc)
     }
    }
    
    println(scanInput(list))  
    

    【讨论】:

      猜你喜欢
      • 2015-12-16
      • 1970-01-01
      • 2016-05-31
      • 2013-04-10
      • 1970-01-01
      • 2018-08-17
      • 1970-01-01
      • 2017-09-11
      • 2013-04-25
      相关资源
      最近更新 更多