如何将基于交叉产品的方法添加到scala集合?

时间:2013-05-06 06:17:39

标签: scala collections enrich-my-library

希望这将是一个关于图书馆拉皮条的简单问题(因为关于该主题的其他问题往往会产生超出我目前技能水平的答案)。

我想要做的就是将集合的交叉产品映射到自身。

val distances = points.crossMap(_ distance _)  // points: List[Point3d]

所以我试图皮条Traversable因此:

implicit def toSelfCrossMappable[A](xs: Traversable[A]) = new {
  def crossMap[B](f: (A, A) => B) = xs.flatMap(a => xs.map(f(a, _)))
}

但是它不起作用(它没有进行隐式转换)而且我不明白为什么不(我对scala很新)。我也尝试了Enriching Scala collections with a method中建议的方法,它留给我:

implicit def toSelfCrossMappable[A, C[A]](xs: C[A])(implicit c: C[A] => Traversable[A]) = new SelfCrossable[A, C[A]](xs)(c)

class SelfCrossable[A, C](xs: C)(implicit c: C => Traversable[A]) {
  def crossMap[B](f: (A, A) => B) = xs.flatMap(a => xs.map(f(a, _)))
}

,但是抛出与我(看起来更简单)方式相同的错误。

我在这里做错了什么?

2 个答案:

答案 0 :(得分:10)

它不漂亮,但可以使用IsTraversableLike

来完成
import scala.language.implicitConversions

import scala.collection.generic.{ CanBuildFrom, IsTraversableLike }
import scala.collection.GenTraversableLike

class SelfCrossMappable[A, Repr](xs: GenTraversableLike[A, Repr]) {
  def crossMap[B, That](f: (A, A) => B)
    (implicit
      cbf: CanBuildFrom[Repr, B, That],
      itl: IsTraversableLike[That] { type A = B }
    ) = xs.flatMap { a => itl.conversion(xs.map(f(a, _)))
  } 
}

implicit def toSelfCrossMappable[Repr](xs: Repr)
  (implicit traversable: IsTraversableLike[Repr]) =
    new SelfCrossMappable(traversable.conversion(xs))

示例REPL会话,

scala> List("foo", "foo", "bar").crossMap(_ == _)
res0: List[Boolean] = List(true, true, false, true, true, false, false, false, true)

答案 1 :(得分:2)

在Scala 2.10中,您可以直接使用隐式类(Miles的回答使用IsTraversableLike助手,这也需要Scala 2.10)。似乎标准集合不需要toSelfCrossMappable(可怕名称BTW)。以下适用于我:

import collection.generic.{CanBuildFrom, IsTraversableLike}
import collection.GenTraversableLike

implicit class CanCrossMap[A, Repr](xs: GenTraversableLike[A, Repr]) {
  def crossMap[B, That](f: (A, A) => B)(
    implicit cbf: CanBuildFrom[Repr, B, That], 
             itl: IsTraversableLike[That] { type A = B }): That = 
      xs.flatMap { a => itl.conversion(xs.map(f(a, _)))
  } 
}

另一种选择是完全忽略IsTraversableLike

import collection.GenTraversableOnce

implicit class CanCrossMap[A, Repr](xs: GenTraversableLike[A, Repr]) {
  def crossMap[B, That <: GenTraversableOnce[B]](f: (A, A) => B)(
    implicit cbf: CanBuildFrom[Repr, B, That]): That = 
      xs.flatMap { a => xs.map(f(a, _))}
}

示例:

Vector((1.0, 2.0), (3.0, 4.0), (5.0, 6.0)).crossMap { case ((ax, ay), (bx, by)) =>
  val dx = bx - ax
  val dy = by - ay
  math.sqrt(dx*dx + dy*dy)
}

Miles答案涵盖了一些额外的案例,其中集合不是直接GenTraversableLike,即ArrayString(尝试用Vector代替Array在最后一个例子中)。