2010-07-12 2 views
7

map과 같은 기능을 Scala List에 추가하려는 경우 이라는 줄에 따라 의 각 요소에 list을 두 번 적용합니다. (A 더 심각한 예는 병렬 또는 분산 맵을 구현 될 수도 있지만 나는 그 방향으로 세부 사항에 의해 산만하지 않습니다.)TraversableLike.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 초 동안, 그리고 우리가 아무것도 Traverseable 작동하지해야 할 이유는 map 기능, 예를 들어와,이 없다 Set 또는 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!) 같은 유형의 개체를 반환 할 일반적인 규칙으로

답변

10

당신은 모든 Traversables에 대해이 작업을 수행 할 수 그들이 돈으로 지도가 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)) 
} 

UPDATE 나는 TraversableW 두 가지 유형 매개 변수보다는 알렉세이의 솔루션으로 하나 개의 매개 변수를 받아들이는 이유를 설명하기 위해 또 다른 포주 방법, mapToString을 추가했습니다. 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)) 
} 

다른 점은 무엇입니까?우리는 단지 Traversable[A]에서 특정 컬렉션 하위 유형을 복구 할 수 없기 때문에 collection.breakOut을 사용해야했습니다.

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 
} 

Builderbmap 통해 동적 유형을 유지하는기구이며 원래 수집하여 초기화된다. 그러나 CanBuildFrom에 대한 모든 지식을 부인했습니다. 형식 인수 Nothing을 통해에서입니다. 당신이 def foo(a: Nothing) = 0를 호출 할 수있는 것보다

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)를 호출 할 수 없습니다 : 당신이 Nothing로 할 수있는 모든 breakOut이 바로 이러한 작업을 수행하는 것 인, 그것을 무시한다.

+0

완벽하게 작동합니다! –

+2

Scalaz에서 약간 다른 접근 방식을 취했습니다. 좀 더 강력합니다. 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

+0

필자는 코드를 편집 할 수있는 권리가 없지만 코드 형식은 아마도 블록을 확장하여 예제에 가져 오기를 포함해야합니까? 건배! – pr1001

5

, 당신은 TraversableLike (IterableLike, SeqLike 필요 , 등) 대신 Traversable. 여기에 내가 가지고 올 수있는 가장 일반적인 버전입니다 (별도의 FancyTraversable 클래스는 구조 유형과 반사 히트 추론 피할 수있다),

class FancyTraversable[A, S <: TraversableLike[A, S]](t: S) { 
    def mapmap(f: A => A)(implicit bf: CanBuildFrom[S,A,S]): S = { t map { a: A => f(f(a)) } } 
} 

implicit def createFancyTraversable[A, S <: TraversableLike[A, S]](t: S): FancyTraversable[A, S] = new FancyTraversable(t) 
+0

무언가를 가져와야합니까? "오류 : 찾을 수 없음 : TraversableLike"유형이 나타납니다. (2.8.0RC7) –

+0

"import scala.collection._"및 "import scala.collection.generic._"은 최소한 컴파일하지만 이제는 "List (1,2,3) mapmap {_ + 1}" "오류 : 값 mapmap이 List [Int]의 멤버가 아닙니다." –

관련 문제