gpt4 book ai didi

scala - 我可以使用具有很好变体类型的 TraversableLike.map 的类似物 "pimp my library"吗?

转载 作者:行者123 更新时间:2023-12-04 10:05:55 24 4
gpt4 key购买 nike

假设我想添加像 map 这样的功能到 Scala List ,类似于list mapmap f ,它应用函数 flist 的每个元素两次。 (一个更严重的例子可能是实现一个并行或分布式 map ,但我不想被那个方向的细节分散注意力。)

我的第一种方法是

object MapMap {
implicit def createFancyList[A](list: List[A]) = new Object {
def mapmap(f: A => A): List[A] = { list map { a: A => f(f(a)) } }
}
}

这现在很好用
scala> import MapMap._
import MapMap._

scala> List(1,2,3) mapmap { _ + 1 }
res1: List[Int] = List(3, 4, 5)

当然,它只适用于 List s,我们没有理由不希望它适用于任何东西 Traverseable , 带有 map功能,例如 Set s 或 Stream s。所以第二次尝试看起来像
object MapMap2 {
implicit def createFancyTraversable[A](t: Traversable[A]) = new Object {
def mapmap(f: A => A): Traversable[A] = { t map { a: A => f(f(a)) } }
}
}

但是现在,当然,结果不能分配给 List[A] :
scala> import MapMap2._
import MapMap2._

scala> val r: List[Int] = List(1,2,3) mapmap { _ + 1 }
<console>:9: error: type mismatch;
found : Traversable[Int]
required: List[Int]

有没有中间立场?我可以编写一个隐式转换,将方法添加到 Traversable 的所有子类,并成功返回该类型的对象吗?

(我猜这需要理解可怕的 CanBuildFrom 特征,甚至可能是 breakout !)

最佳答案

您不能对所有 Traversable 执行此操作,因为它们不保证 map 返回比 Traversable 更具体的任何内容。请参阅下面的更新 2。

import collection.generic.CanBuildFrom
import collection.TraversableLike

class TraversableW[CC[X] <: TraversableLike[X, CC[X]], A](value: CC[A]) {
def mapmap(f: A => A)(implicit cbf: CanBuildFrom[CC[A], A, CC[A]]): CC[A]
= value.map(f andThen f)
def mapToString(implicit cbf: CanBuildFrom[CC[A], String, CC[String]]): CC[String]
= value.map(_.toString)
}

object TraversableW {
implicit def TraversableWTo[CC[X] <: TraversableLike[X, CC[X]], A](t: CC[A]): TraversableW[CC, A]
= new TraversableW[CC, A](t)
}

locally {
import TraversableW._

List(1).mapmap(1+)
List(1).mapToString
// The static type of Seq is preserved, *and* the dynamic type of List is also
// preserved.
assert((List(1): Seq[Int]).mapmap(1+) == List(3))
}

更新
我添加了另一种拉皮条方法, mapToString , 来说明为什么 TraversableW接受两个类型参数,而不是 Alexey 的解决方案中的一个参数。参数 CC是更高种类的类型,它代表原始集合的容器类型。第二个参数 A , 表示原始集合的元素类型。方法 mapToString因此能够返回具有不同元素类型的原始容器类型: CC[String .

更新 2
感谢@oxbow_lakes 评论,我重新考虑了这一点。确实可以直接拉皮条 CC[X] <: Traversable[X] , TraversableLike不是严格需要的。内联评论:
import collection.generic.CanBuildFrom
import collection.TraversableLike

class TraversableW[CC[X] <: Traversable[X], A](value: CC[A]) {
/**
* A CanBuildFromInstance based purely the target element type `Elem`
* and the target container type `CC`. This can be converted to a
* `CanBuildFrom[Source, Elem, CC[Elem]` for any type `Source` by
* `collection.breakOut`.
*/
type CanBuildTo[Elem, CC[X]] = CanBuildFrom[Nothing, Elem, CC[Elem]]

/**
* `value` is _only_ known to be a `Traversable[A]`. This in turn
* turn extends `TraversableLike[A, Traversable[A]]`. The signature
* of `TraversableLike#map` requires an implicit `CanBuildFrom[Traversable[A], B, That]`,
* specifically in the call below `CanBuildFrom[Traversable[A], A CC[A]`.
*
* Essentially, the specific type of the source collection is not known in the signature
* of `map`.
*
* This cannot be directly found instead we look up a `CanBuildTo[A, CC[A]]` and
* convert it with `collection.breakOut`
*
* In the first example that referenced `TraversableLike[A, CC[A]]`, `map` required a
* `CanBuildFrom[CC[A], A, CC[A]]` which could be found.
*/
def mapmap(f: A => A)(implicit cbf: CanBuildTo[A, CC]): CC[A]
= value.map[A, CC[A]](f andThen f)(collection.breakOut)
def mapToString(implicit cbf: CanBuildTo[String, CC]): CC[String]
= value.map[String, CC[String]](_.toString)(collection.breakOut)
}

object TraversableW {
implicit def TraversableWTo[CC[X] <: Traversable[X], A](t: CC[A]): TraversableW[CC, A]
= new TraversableW[CC, A](t)
}

locally {
import TraversableW._

assert((List(1)).mapmap(1+) == List(3))

// The static type of `Seq` has been preserved, but the dynamic type of `List` was lost.
// This is a penalty for using `collection.breakOut`.
assert((List(1): Seq[Int]).mapmap(1+) == Seq(3))
}

有什么不同?我们不得不使用 collection.breakOut ,因为我们无法从仅仅 Traversable[A] 中恢复特定的集合子类型.
def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = {
val b = bf(repr)
b.sizeHint(this)
for (x <- this) b += f(x)
b.result
}
Builder b使用原始集合进行初始化,这是通过 map 保留动态类型的机制.但是,我们的 CanBuildFrom通过类型参数 Nothing 拒绝所有关于 From 的知识. Nothing 的所有功能是忽略它,这正是 breakOut做:
def breakOut[From, T, To](implicit b : CanBuildFrom[Nothing, T, To]) =
new CanBuildFrom[From, T, To] {
def apply(from: From) = b.apply();
def apply() = b.apply()
}

我们无法调用 b.apply(from) , 不超过你可以调用 def foo(a: Nothing) = 0 .

关于scala - 我可以使用具有很好变体类型的 TraversableLike.map 的类似物 "pimp my library"吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3225675/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com