3

我有一个函数,它以 aSeq[_]作为参数并返回一个不可变的类实例,并将 thisSeq作为 val 成员。如果Seq是可变的,我显然想创建一个防御性副本以保证我的返回类实例不能被修改。

这种模式的最佳实践是什么?首先,我很惊讶无法重载函数

  def fnc(arg: immutable.Seq[_]) = ...
  def fnc(arg: mutable.Seq[_]) = ...

我也可以进行模式匹配:

  def fnc(arg: Seq[_]) = arg match {
    case s: immutable.Seq[_] => { println("immutable"); s}
    case s: mutable.Seq[_] => {println("mutable"); List()++s }
    case _: ?
  }   

但我不确定这个_案子。是否保证argimmutable.Seqmutable.Seq?我也不知道是否List()++s是正确的转换方法。我在 SO 上看到了很多帖子,但其中大多数是针对 2.8 或更早版本的。

Scala-Collections 是否足够“智能”,以至于我总是可以(没有模式匹配)编写List()++s,如果不可变,我得到相同的实例,如果可变,我得到一个深拷贝?

推荐的方法是什么?

4

3 回答 3

4

如果要同时支持两者,则需要进行模式匹配。的代码Seq() ++保证(作为其 API 的一部分)如果它是不可变的,它不会复制其余部分:

scala> val v = Vector(1,2,3)
v: scala.collection.immutable.Vector[Int] = Vector(1, 2, 3)

scala> Seq() ++ v
res1: Seq[Int] = List(1, 2, 3)

它可能会在某些特殊情况下进行模式匹配,但您知道自己想要的情况。所以:

def fnc[A](arg: Seq[A]): Seq[A] = arg match {
  case s: collection.immutable.Seq[_] => arg
  case _ => Seq[A]() ++ arg
}

您不必担心_; 这只是说你不关心类型参数是什么(不是你可以检查),如果你这样写,你不会:如果不可变则通过,否则复制。

于 2013-01-13T17:46:26.993 回答
1

这种模式的最佳实践是什么?

如果你想保证不变性,最好的做法是制作一个防御性副本,或者 require immutable.Seq

但我不确定 _ 案。是否保证 arg 是 immutable.Seq 或 mutable.Seq?

不一定,但我相信每个继承自的标准库集合collection.Seq也继承自这两者之一。然而,自定义集合理论上可以继承自collection.Seq. 有关模式匹配解决方案的改进,请参阅 Rex 的答案。

Scala-Collections 是否足够“智能”,以至于我可以总是(没有模式匹配)编写 List()++s,如果不可变,我得到相同的实例,如果可变,我得到一个深拷贝?

似乎它们在某些情况下是,但在其他情况下不是,例如:

val immutableSeq = Seq[Int](0, 1, 2)
println((Seq() ++ immutableSeq) eq immutableSeq) // prints true
val mutableSeq = mutable.Seq[Int](0, 1, 2)
println((Seq() ++ mutableSeq) eq mutableSeq) // prints false

eq引用相等在哪里。请注意,上述内容也适用于List() ++ s,但是正如 Rex 指出的那样,它不适用于所有集合,例如Vector.

于 2013-01-13T09:39:06.117 回答
0

你当然可以用那种方式超载!例如,这编译得很好:

object  MIO
{
  import collection.mutable

  def f1[A](s: Seq[A]) = 23
  def f1[A](s: mutable.Seq[A]) = 42

  def f2(s: Seq[_]) = 19
  def f2(s: mutable.Seq[_]) = 37
}

在 REPL 中:

Welcome to Scala version 2.10.0 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_37).
Type in expressions to have them evaluated.
Type :help for more information.

scala> import rrs.scribble.MIO._; import collection.mutable.Buffer
import rrs.scribble.MIO._
import collection.mutable.Buffer

scala> f1(List(1, 2, 3))
res0: Int = 23

scala> f1(Buffer(1, 2, 3))
res1: Int = 42

scala> f2(List(1, 2, 3))
res2: Int = 19

scala> f2(Buffer(1, 2, 3))
res3: Int = 37
于 2013-01-13T15:53:12.547 回答