Bạn không thể làm điều này cho tất cả Traversables, như họ sẽ không bao không đảm bảo rằng bản đồ trả về bất cứ điều gì cụ thể hơn Traversable.
Xem Cập nhật 2 bên dưới.
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))
}
CẬP NHẬT Tôi đã thêm một phương pháp pimped, mapToString
, để chứng minh tại sao TraversableW
chấp nhận hai tham số kiểu, chứ không phải là một tham số như trong dung dịch Alexey của. Tham số CC
là loại được phân loại cao hơn, nó đại diện cho loại vùng chứa của tập hợp gốc. Tham số thứ hai, A
, thể hiện loại phần tử của tập hợp gốc. Do đó, phương thức mapToString
có thể trả về loại vùng chứa ban đầu với loại phần tử khác: CC[String
.
CẬP NHẬT 2 Nhờ nhận xét @oxbow_lakes, tôi đã suy nghĩ lại điều này. Nó thực sự là có thể trực tiếp pimp CC[X] <: Traversable[X]
, TraversableLike
là không cần thiết. Nhận xét nội tuyến:
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))
}
Sự khác biệt là gì?Chúng tôi đã phải sử dụng collection.breakOut
, vì chúng tôi không thể khôi phục tập hợp con phụ cụ thể từ một chỉ 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
}
Các Builder
b
được khởi tạo với bộ sưu tập ban đầu, đó là cơ chế để bảo tồn các loại động thông qua một map
. Tuy nhiên, CanBuildFrom
của chúng tôi đã từ chối tất cả kiến thức về Từ, theo cách đối số loại Nothing
. Tất cả các bạn có thể làm với Nothing
là lờ nó đi, đó là chính xác những gì breakOut
làm:
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()
}
Chúng ta không thể gọi b.apply(from)
, không nhiều hơn bạn có thể gọi def foo(a: Nothing) = 0
.
Hoạt động hoàn hảo! –
Tôi đã thực hiện một cách tiếp cận hơi khác trong Scalaz, đó là một chút mạnh mẽ hơn: http://github.com/scalaz/scalaz/blob/master/core/src/main/scala/scalaz/CanBuildAnySelf.scala#L24 http: //github.com/scalaz/scalaz/blob/master/core/src/main/scala/scalaz/Functor.scala#L28 – retronym
Tôi không có quyền chỉnh sửa, viết lại, nhưng có lẽ định dạng mã khối cần được mở rộng để bao gồm nhập khẩu trong ví dụ của bạn? Chúc mừng! – pr1001