14

在 Odersky 等人的 Scala 书中,他们说使用列表。我没有读完这本书的封面,但所有的例子似乎都使用了 val List。据我了解,还鼓励使用 vals 而不是 vars。但在大多数应用程序中,使用 var List 或 val MutableList 之间没有权衡吗?显然,我们尽可能使用 val 列表。但是使用大量的 var 列表(或 var 向量等)是一种好习惯吗?

我对来自 C# 的 Scala 很陌生。我有很多:

public List<T> myList {get; private set;}

如果 C# 内置了不可变性,则可以很容易地将集合声明为 val,因为集合本身在构造后从未更改,即使在其生命周期中会从集合中添加和减去元素。因此,声明一个 var 集合几乎感觉像是离不变性迈出了一步。

针对回答和评论,Scala 的一大卖点是:它可以带来很多好处,而无需像 Lisp 或 Haskell 那样完全改变编写代码的方式。

4

5 回答 5

17

使用大量 var 列表(或 var 向量等)是一种好习惯吗?

我会说使用var不可变集合比使用val可变集合更好。在我的脑海中,因为

  • 你对行为有更多保证:如果你的对象有一个可变列表,你永远不知道其他外部对象是否会更新它

  • 你限制了可变性的程度;返回集合的方法将产生一个不可变的集合,因此您的一个对象内只有可变性

  • 通过简单地将 var 分配给 val 很容易使 var 不可变,而要使可变集合不可变,您必须使用不同的集合类型并重新构建它

在某些情况下,例如具有大量 I/O 的时间相关应用程序,最简单的解决方案是使用可变状态。在某些情况下,可变解决方案更加优雅。但是,在大多数代码中,您根本不需要可变性。关键是使用具有更高阶函数的集合而不是循环,或者如果不存在合适的函数则使用递归。这比听起来简单。您只需要花一些时间了解 List (以及其他集合,它们大多相同)上的方法。最重要的是:

map:将您提供的函数应用于集合中的每个元素 - 使用而不是循环和更新数组中的值

foldLeft:从集合中返回单个结果 - 使用而不是循环和更新累加器变量

for-yield表达式:简化您的映射和过滤,尤其是对于嵌套循环类型的问题

归根结底,大部分函数式编程都是不变性的结果,你不能真正拥有一个没有另一个;然而,局部变量主要是一个实现细节:只要它不能脱离局部范围,一点可变性就没有错。所以使用带有不可变集合的变量,因为本地变量不会被导出。

于 2012-06-12T18:09:34.157 回答
8

您假设List必须是可变的,或者指向它的任何内容都必须是可变的。换句话说,您需要选择以下两个选项之一:

val list: collection.mutable.LinkedList[T]
var list: List[T]

这是一种错误的二分法。你可以同时拥有:

val list: List[T]

所以,你应该问的问题是我该怎么做?,但我们只能在您尝试并遇到特定问题时回答。没有通用的答案可以解决您的所有问题(嗯,有——单子和递归——但这通用了)。

所以试试吧。您可能有兴趣查看Code Review,其中大多数 Scala 问题都与如何使某些代码更具功能性有关。但是,归根结底,我认为最好的方法是尝试,当您无法解决某些特定问题时求助于 Stack Overflow。

于 2012-06-12T15:12:07.890 回答
1

以下是我如何看待函数式编程中的可变性问题。

最佳解决方案:值是最好的,所以函数式编程中最好的用法是值和递归函数:

val myList = func(4); 
def func(n) = if (n>0) n::func(n) else Nil

需要可变的东西:有时需要可变的东西,或者让一切变得更容易。当我们面对这种情况时,我的印象是使用 mutables 结构,所以使用val list: collection.mutable.LinkedList[T]而不是var list: List[T],这不是因为性能上的真正改进,而是因为 mutable 集合中已经定义了可变函数。

这个建议是个人的,当你想要性能时可能不推荐,但它是我在 scala 中用于日常编程的指南。

于 2012-06-12T16:13:28.687 回答
1

我相信您无法将可变val/不可变的问题var与特定用例分开。让我再深入一点:你想问自己两个问题:

  1. 我如何将我的收藏暴露在外面?
    1. 我想要当前状态的快照,并且无论对托管集合的实体所做的更改如何,此快照都不应更改。在这种情况下,您应该更喜欢 immutable var。原因是使用可变的唯一方法val是通过防御性副本。
    2. 我想要一个关于状态的视图,它应该改变以反映对原始对象状态的更改。在这种情况下,您应该选择 immutable val,并通过不可修改的包装器返回它(很像Collections.unmodifiableList()Java 中的做法,这里有一个问题,询问如何在 Scala 中这样做)。我认为没有办法使用不可变的 var 来实现这一点,所以我相信你在这里的选择是强制性的。
    3. 我只关心有没有副作用。在这种情况下,这两种方法非常相似。使用不可变的var,您可以直接返回内部表示,因此可能会更清晰一些。
  2. 我如何修改我的收藏?
    1. 我通常进行批量更改,即一次设置整个集合。这使得 immutablevar成为更好的选择:您只需将输入中的内容分配给当前状态,就可以了。使用不可变的val,您需要先清除您的收藏,然后将新内容复制进去。肯定更糟。
    2. 我通常会进行逐点更改,即向/从集合中添加/删除单个元素(或其中几个元素)。这是我大多数时候实际看到的,集合只是一个实现细节,也是trait唯一暴露其状态逐点操作的方法。在这种情况下,一个可变的val可能通常在性能方面更好,但如果这是一个问题,我建议看看Scala 的集合性能
于 2017-03-27T09:27:10.373 回答
0

如果有必要使用 var 列表,为什么不呢?为了避免问题,您可以例如限制变量的范围。前段时间有一个类似的问题,有一个很好的答案:scala's mutable and immutable set when to use val and var

于 2012-06-12T15:13:14.130 回答