Scala:var List vs val MutableList

时间:2012-06-12 14:42:38

标签: scala collections immutability

在Odersky等人的Scala书中,他们说使用列表。我还没有阅读书籍封面,但所有的例子似乎都使用了val List。据我所知,人们也鼓励在vars上使用vals。但是在大多数应用程序中,使用var List或val MutableList之间没有权衡。显然我们可以使用val List。 但是使用大量var列表(或var Vectors等)是不错的做法?

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

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

如果C#内置不变性,很容易被声明为val的集合,因为集合本身在构造之后从未改变过,即使元素在其生命周期中被添加和减去。因此,声明var集合几乎感觉就像离不变性一样。

作为对答案和评论的回应,Scala的一个强大卖点是:它可以带来很多好处,而不必像Lisp或Haskell那样完全改变编写代码的方式。

5 个答案:

答案 0 :(得分:15)

  

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

我认为将var与不可变集合一起使用比将val与可变集合一起使用更好。在我的头顶,因为

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

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

  • 通过简单地将var赋值给val来使var变得容易,而要使可变集合成为不可变的,你必须使用不同的集合类型并重建它

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

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

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

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

最终,大部分功能编程都是不变性的结果,你不可能没有另一个;但是,本地变量主要是一个实现细节:只要它不能从本地范围中逃脱,就有一点可变性没有错。因此,使用具有不可变集合的变量,因为本地变量不是将要导出的变量。

答案 1 :(得分:8)

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

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

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

val list: List[T]

所以,您应该问的问题是我该怎么做?,但我们只能在您尝试并面对特定问题时回答这个问题。没有通用的答案可以解决你所有的问题(好吧,有 - monad和递归 - 但是通用)。

所以......试一试。您可能有兴趣查看Code Review,其中大多数Scala问题都与如何使某些代码更具功能性有关。但是,最终,我认为最好的方法是尝试,并在你无法弄清楚某些特定问题时诉诸Stack Overflow。

答案 2 :(得分: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],这不是因为对性能的真正改进,而是因为已经定义的可变函数可变的集合。

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

答案 3 :(得分:1)

我相信您不能将可变val /不可变var的问题与特定用例区分开来。让我加深一点:你想问自己有两个问题:

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

答案 4 :(得分:0)

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