Как добавить кросс-продуктовые методы в коллекции Scala?

Надеюсь, это будет простой вопрос о том, как разворачивать библиотеку (потому что другие вопросы по этой теме, как правило, дают ответы за пределами моего текущего уровня навыков).

Все, что я хочу сделать, это сопоставить перекрестный продукт коллекции с самим собой.

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). Я также попробовал метод, предложенный в Обогащении коллекций Scala, с методом, который оставил мне:

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 ответа

Решение

Это не красиво, но это можно сделать с 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)

В Scala 2.10 вы можете напрямую использовать неявные классы (ответ Майлза использует 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)
}

Ответ Майлза охватывает несколько дополнительных случаев, когда коллекция не является GenTraversableLikeа именно Array а также String (попробуйте заменить Vector за Array в последнем примере).

Другие вопросы по тегам