Как добавить кросс-продуктовые методы в коллекции 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
в последнем примере).