为什么Scala维护集合类型不返回Iterable(如.Net)?

Mar*_*cek 6 collections scala

在Scala中,你可以做到

val l = List(1, 2, 3)
l.filter(_ > 2)               // returns a List[Int]
val s = Set("hello", "world")
s.map(_.length)               // returns a Set[Int]
Run Code Online (Sandbox Code Playgroud)

问题是:为什么这有用?

Scala集合可能是唯一执行此操作的现有集合框架.Scala社区似乎同意需要此功能.然而,似乎没有人会错过其他语言的这种功能.示例C#(修改命名以匹配Scala):

var l = new List<int> { 1, 2, 3 }
l.filter(i => i > 2)          // always returns Iterable[Int]
l.filter(i => i > 2).toList   // if I want a List, no problem
l.filter(i => i > 2).toSet    // or I want a Set
Run Code Online (Sandbox Code Playgroud)

在.NET中,我总是得到一个Iterable,这取决于我想用它做什么.(这也使.NET集合非常简单).

使用Set的Scala示例强制我从一组字符串中创建一组长度.但是,如果我只想迭代长度,或构建长度列表,或保持Iterable以后过滤它,该怎么办?立即构建一个集合似乎毫无意义.(编辑:collection.view提供更简单的.NET功能,很好)

我相信你会向我展示.NET方法绝对错误或杀死性能的例子,但我看不到任何问题(使用.NET多年).

Jea*_*let 10

对你的问题不是一个完整的答案,但Scala从不强迫你使用一种集合类型而不是另一种集合类型.你可以自由编写这样的代码:

import collection._
import immutable._

val s = Set("hello", "world")
val l: Vector[Int] = s.map(_.length)(breakOut)
Run Code Online (Sandbox Code Playgroud)

了解更多关于breakOut丹尼尔索布拉尔的详细回答了另一个问题.

如果您想要懒惰地评估mapfilter评估,请使用:

s.view.map(_.length)
Run Code Online (Sandbox Code Playgroud)

这整个行为使得集成新的集合类变得容易,并且继承了标准集合的所有强大功能而没有代码重复,所有这些都确保YourSpecialCollection#filter返回一个实例YourSpecialCollection; 该YourSpecialCollection#map返回的实例YourSpecialCollection,如果它支持被映射到的类型,或者一个内置的备用集合,如果它不(像调用你发生什么事mapBitSet).当然,C#迭代器没有.toMySpecialCollection方法.

另请参阅:Scala集合体系结构中的 "集成新集和映射" .

  • @Martin请注意,经常使用视图和惰性迭代器(尤其是以链式方式)可能对小型集合的性能不利,因为创建中间集合通常不会非常昂贵.并感谢有关扩展方法的提示!请注意,在具有转义分析的现代JVM上,Scala的pimp-my-library模式不会导致额外的分配. (4认同)
  • "一个C#迭代器没有.toMySpecialCollection" - 你可以使用pimp我的库模式(在C#中称为扩展方法).扩展方法不涉及每次调用的额外分配. (2认同)

soc*_*soc 9

Scala遵循"统一返回类型原则",确保您始终以适当的返回类型结束,而不是像C#中那样丢失该信息.

C#这样做的原因是它们的类型系统不足以提供这些保证而不会覆盖每个子类中每个方法的整个实现.Scala通过使用更高的类型来解决这个问题.

为什么Scala有唯一的集合框架呢?因为它比大多数人认为的更难,特别是当字符串和数组之类的东西不是"真正的"集合时也应该集成:

// This stays a String:
scala> "Foobar".map(identity)
res27: String = Foobar
// But this falls back to the "nearest" appropriate type:
scala> "Foobar".map(_.toInt)
res29: scala.collection.immutable.IndexedSeq[Int] = Vector(70, 111, 111, 98, 97, 114)
Run Code Online (Sandbox Code Playgroud)


Lan*_*dei 7

如果你有一个Set,并且对它的操作返回一段Iterable时间它的运行时类型仍然是a Set,那么你将丢失有关其行为的重要信息,以及对特定于集合的方法的访问.

BTW:有其他语言的表现类似,比如Haskell,影响斯卡拉很多.Haskell版本map看起来像翻译成Scala(没有implicit魔法):

//the functor type class
trait Functor[C[_]] {
   def fmap[A,B](f: A => B, coll: C[A]) : C[B]
}

//an instance  
object ListFunctor extends Functor[List] {
   def fmap[A,B](f: A => B, list: List[A]) : List[B] = list.map(f)
}

//usage 
val list = ListFunctor.fmap((x:Int) => x*x, List(1,2,3))
Run Code Online (Sandbox Code Playgroud)

我认为Haskell社区也重视此功能:-)