Scala 中的不可变列表
Immutable Lists in Scala
我只是想弄清楚像列表这样的不可变事物是如何工作的,以及如何向它添加东西?
很抱歉问了这么愚蠢的问题,但是为什么我的列表打印出来的时候总是空的?
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)
}
对于我的不便和这个相当愚蠢的问题,我们深表歉意!
原因
println(list)
只打印出一个空列表是因为位
input :: list
实际上并没有改变列表本身。在这种情况下,它只是非常临时地创建一个包含前面输入的列表。
尝试
println(input :: list)
或
val newList = input :: list
println(newList)
你会明白我的意思的。
在 Scala 中,列表是不可变的。
那我怎样才能将项目添加到列表中呢?
当您将一个项目添加到列表中时,一个新的 List
实例将以一个项目作为其头部,其尾部现在包含之前的列表。
如果您有内部称为 intList
的“1,2,3”列表,它表示为
List(3, List(2, List(1, Nil) ) )
如果你添加一个元素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 在线课程
尝试以更实用的方式重写代码。对不可变数据结构的每个操作 return 具有更改的新实例。所以 ::
运算符创建了一个新的列表,前面有 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 函数式风格。
I am just trying to figure out how immutable things like a List are working, and how I can add things to it?
你不能。毕竟,这就是 immutable 的意思。如果您不喜欢拉丁语,immutable 的英文翻译是 unchangeable。现在应该清楚了,为什么你不能改变一些不可改变的。
I am very sorry for asking such dumb questions, but why is here my list always empty when printing it out?
您创建了一个空列表,并且永远不会更改它(因为它 无论如何都无法 更改)。所以,当然是空的。
但是,您 可以 做的是创建一个 new 列表,它几乎与 old 完全一样 列表,除了在前面添加了一个新项目。这就是你在这里所做的:
input :: list
但是,您没有在任何地方分配这个新列表,您没有 return 它,您完全忽略了它。
如果你想以任何方式实际使用你的列表,你需要以某种方式记住它。最明显的解决方案是将其分配给一个变量:
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 的实现必须消除直接尾递归。
感谢大家的帮助,非常感谢大家的帮助!
我应该仔细研究一下递归,因为它似乎真的很重要,就像在 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))
我只是想弄清楚像列表这样的不可变事物是如何工作的,以及如何向它添加东西?
很抱歉问了这么愚蠢的问题,但是为什么我的列表打印出来的时候总是空的?
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)
}
对于我的不便和这个相当愚蠢的问题,我们深表歉意!
原因
println(list)
只打印出一个空列表是因为位
input :: list
实际上并没有改变列表本身。在这种情况下,它只是非常临时地创建一个包含前面输入的列表。
尝试
println(input :: list)
或
val newList = input :: list
println(newList)
你会明白我的意思的。
在 Scala 中,列表是不可变的。
那我怎样才能将项目添加到列表中呢?
当您将一个项目添加到列表中时,一个新的 List
实例将以一个项目作为其头部,其尾部现在包含之前的列表。
如果您有内部称为 intList
的“1,2,3”列表,它表示为
List(3, List(2, List(1, Nil) ) )
如果你添加一个元素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 在线课程
尝试以更实用的方式重写代码。对不可变数据结构的每个操作 return 具有更改的新实例。所以 ::
运算符创建了一个新的列表,前面有 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 函数式风格。
I am just trying to figure out how immutable things like a List are working, and how I can add things to it?
你不能。毕竟,这就是 immutable 的意思。如果您不喜欢拉丁语,immutable 的英文翻译是 unchangeable。现在应该清楚了,为什么你不能改变一些不可改变的。
I am very sorry for asking such dumb questions, but why is here my list always empty when printing it out?
您创建了一个空列表,并且永远不会更改它(因为它 无论如何都无法 更改)。所以,当然是空的。
但是,您 可以 做的是创建一个 new 列表,它几乎与 old 完全一样 列表,除了在前面添加了一个新项目。这就是你在这里所做的:
input :: list
但是,您没有在任何地方分配这个新列表,您没有 return 它,您完全忽略了它。
如果你想以任何方式实际使用你的列表,你需要以某种方式记住它。最明显的解决方案是将其分配给一个变量:
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 的实现必须消除直接尾递归。
感谢大家的帮助,非常感谢大家的帮助! 我应该仔细研究一下递归,因为它似乎真的很重要,就像在 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))