Scala中的不可变列表

时间:2015-09-17 15:59:14

标签: list scala add immutability

我只想弄清楚像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)

}

对于我的不便和这个相当愚蠢的问题感到抱歉!

5 个答案:

答案 0 :(得分:6)

  

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

你不能。毕竟, immutable 意味着什么。如果拉丁文不是你的一杯茶, immutable 的英文翻译是不可更改的。现在应该清楚,为什么你不能改变不可改变的东西

  

我很遗憾地问这些愚蠢的问题,但是为什么我的列表在打印出来时总是空的?

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

可以做什么,但是,创建列表几乎与列表完全相同项目前置于前面。这就是你在这里做的事情:

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 变量......现在,我们刚刚改变了可变性。

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

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

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

答案 1 :(得分:2)

原因

println(list)

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

input :: list

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

尝试

println(input :: list) 

val newList = input :: list
println(newList)

你会明白我的意思。

答案 2 :(得分:1)

在scala中,List是不可变的。

然后我如何将项目添加到列表中?

当您添加要列出的项目时,新的List实例将以项目为首,其尾部现在包含上一个列表。

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

  

列表(3,列表(2,列表(1,无)))

如果您向此4

添加元素intList
  

List(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

答案 3 :(得分:1)

尝试以更多功能方式重写代码。不可变数据结构上的每个操作都会返回带有更改的新实例。因此,::运营商会在前面创建包含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 :(得分:0)

感谢您的帮助,感谢大家的帮助! 我应该仔细看看递归,因为它似乎非常重要,就像在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))