映射集合时避免意外删除重复项

And*_*yle 30 collections functional-programming scala

我非常喜欢函数式编程概念,但是当我在一个集合中进行映射时Set(即自动删除重复项),我现在被同一个问题所困在两个不同的场合.问题是在转换这样一个集合的元素之后,输出容器也是一个集合,因此删除了转换输出的任何重复.

一个非常简短的REPL会议来说明这个问题:

scala> case class Person(name: String, age: Int)
defined class Person

scala> val students = Set(Person("Alice", 18), Person("Bob", 18), Person("Charles", 19))
students: scala.collection.immutable.Set[Person] = Set(Person(Alice,18), Person(Bob,18), Person(Charles,19))

scala> val totalAge = (students map (_.age)).sum
totalAge: Int = 37
Run Code Online (Sandbox Code Playgroud)

我当然希望总年龄为18 + 18 + 19 = 55,但因为学生 们存放在a中Set,所以他们的年龄也是如此,因此其中一个18在年龄之前消失了.

在实际代码中,这通常更加隐蔽,更难以发现,特别是如果您编写实用程序代码,它只需要Traversable和/或使用声明返回的方法的输出Traversable(其实现恰好是Set).在我看来,这些情况几乎不可能被发现,直到/除非它们表现为一个错误.

那么,是否有任何最佳实践可以减少我对这个问题的影响?我是否错误地考虑map将一般的Traversable概念化地转换为每个元素,而不是将转换后的元素依次添加到一些新的集合中?.toStream如果我想保留这个心理模型,我应该在映射之前调用所有内容吗?

任何提示/建议将不胜感激.

更新:到目前为止,大多数答案都集中在将总和中包含重复项的机制上.在一般情况下编写代码时,我对所涉及的实践更感兴趣 - 你是否自己在调用之前总是调用toList每个集合map?在调用方法之前,您是否经常检查应用程序中所有集合的具体类?等等.

修复已经被识别为问题的东西是微不足道的 - 困难的部分是防止这些错误首先蔓延.

oxb*_*kes 19

您可能希望将scalaz foldMap用于此目的,因为它适用于任何有Foldable类型类可用的东西.您案例中的用法如下所示:

persons foldMap (_.age)
Run Code Online (Sandbox Code Playgroud)

签名foldMap如下:

trait MA[M[_], A] {
  val value: M[A]

  def foldMap[B](f: A => B)(implicit f: Foldable[M], m: Monoid[B])
}
Run Code Online (Sandbox Code Playgroud)

所以; 只要你有一定的收藏CC[A]哪里CC可以折叠起来(即穿越),从功能A => B,其中B为幺,你可以积累的结果.

  • 是的,你_ + _.年龄较短,但我认为简洁与可读性之间存在平衡. (3认同)
  • `(0 /:students)(_ + _.age)`甚至更短 (2认同)

Vik*_*ang 11

不要将额外的依赖项拖到图片中:

(0 /: students) { case (sum, s) => sum + s.age }
Run Code Online (Sandbox Code Playgroud)