대답은 다음의 정의에 있습니다 map.
def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That
여기에는 두 개의 매개 변수가 있습니다. 첫 번째는 함수이고 두 번째는 암시 적입니다. 암시 적으로 제공하지 않으면 스칼라는 사용 가능한 가장 구체적인 것을 선택합니다 .
약 breakOut
그래서, 목적은 breakOut무엇입니까? 질문에 주어진 예를 고려하십시오. 문자열 목록을 가져 와서 각 문자열을 튜플로 변환 (Int, String)한 다음 그중 하나를 생성 Map하십시오. 가장 확실한 방법은 중개자 List[(Int, String)]컬렉션을 생성 한 다음 변환하는 것입니다.
점을 감안 map사용하는이 Builder결과 수집을 생산하기 위해서는 중간을 건너 뛸 수 없을 것 List과에 직접 결과를 수집 Map? 분명히 그렇습니다. 그러나 그렇게하려면에 적절한 값 CanBuildFrom을 전달해야합니다 . map이것이 바로 그 일 breakOut입니다.
다음의 정의를 살펴 보자 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()
}
참고 breakOut파라미터, 그리고의 인스턴스를 반환한다 CanBuildFrom. 공교롭게도, 종류는 From, T그리고 To우리가 알고 있기 때문에 이미 추정 된 map기대하고있다 CanBuildFrom[List[String], (Int, String), Map[Int, String]]. 따라서:
From = List[String]
T = (Int, String)
To = Map[Int, String]
결론적으로 breakOut자체적으로 받은 암시를 살펴 보자 . 유형 CanBuildFrom[Nothing,T,To]입니다. 우리는 이미 이러한 유형을 모두 알고 있으므로 암시 적 유형이 필요하다고 판단 할 수 있습니다.CanBuildFrom[Nothing,(Int,String),Map[Int,String]] . 그러나 그러한 정의가 있습니까?
CanBuildFrom의 정의를 보자 .
trait CanBuildFrom[-From, -Elem, +To]
extends AnyRef
따라서 CanBuildFrom첫 번째 유형 매개 변수에는 모순이 있습니다. Nothing하위 클래스 이므로 (즉, 모든 하위 클래스이므로) 모든 클래스를 대신 사용할 수 있습니다 Nothing.
그러한 빌더가 존재하므로 Scala는이를 사용하여 원하는 출력을 생성 할 수 있습니다.
건축업자에 관하여
스칼라 컬렉션 라이브러리의 많은 메소드는 원래 컬렉션을 가져 와서 어떻게 든 처리하고 ( map각 요소를 변환 하는 경우 ) 결과를 새 컬렉션에 저장하는 것으로 구성됩니다.
코드 재사용을 최대화하기 위해이 결과 저장은 기본적으로 요소 추가 및 결과 콜렉션 리턴의 두 가지 조작을 지원 하는 빌더 ( scala.collection.mutable.Builder)를 통해 수행됩니다 . 이 결과 콜렉션의 유형은 빌더 유형에 따라 다릅니다. 따라서 List빌더는을 리턴 List하고 Map빌더는을 리턴합니다 Map. map메소드 구현은 결과 유형과 관련이있을 필요가 없습니다. 빌더가이를 처리합니다.
반면에, map이 빌더를 어떻게 든 받아야합니다. Scala 2.8 Collection을 디자인 할 때 직면 한 문제는 가능한 최고의 빌더를 선택하는 방법이었습니다. 예를 Map('a' -> 1).map(_.swap)들어을 쓰면 Map(1 -> 'a')답장 을 받고 싶습니다 . 반면에는 a Map('a' -> 1).map(_._1)를 반환 할 수 없습니다 Map(을 반환합니다 Iterable).
Builder알려진 유형의 표현에서 최상의 결과를 만들어내는 마술은 이 CanBuildFrom암시 적 방법을 통해 수행됩니다 .
약 CanBuildFrom
무슨 일이 일어나고 있는지 더 잘 설명하기 위해 매핑되는 컬렉션이 Map대신에 예제 가 List있습니다. List나중에 다시 돌아가겠습니다 . 지금은 다음 두 가지 표현을 고려하십시오.
Map(1 -> "one", 2 -> "two") map Function.tupled(_ -> _.length)
Map(1 -> "one", 2 -> "two") map (_._2)
첫 번째는 a를 반환하고 두 번째는 a Map를 반환합니다 Iterable. 피팅 컬렉션을 반환하는 마술은의 작품입니다 CanBuildFrom. map그것을 이해하기 위해 다시 정의를 고려해 봅시다 .
이 메소드 map는에서 상속됩니다 TraversableLike. 그것은 나타내는 파라미터 B와 That, 그리고 차종은 형식 매개 변수의 사용 A과 Repr클래스를 파라미터. 두 정의를 함께 봅시다 :
클래스 TraversableLike는 다음과 같이 정의됩니다.
trait TraversableLike[+A, +Repr]
extends HasNewBuilder[A, Repr] with AnyRef
def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That
어디 A에서 Repr왔는지 이해하기 위해 Map자체 정의를 고려해 보겠습니다 .
trait Map[A, +B]
extends Iterable[(A, B)] with Map[A, B] with MapLike[A, B, Map[A, B]]
때문에이 TraversableLike확장하는 모든 특성을 상속 Map, A그리고 Repr그 중 하나에서 상속 할 수있다. 그러나 마지막 것은 선호도를 얻습니다. 따라서 불변의 정의 Map와 그것을 연결하는 모든 특성 에 따라 다음과 같은 결과를 얻 습니다 TraversableLike.
trait Map[A, +B]
extends Iterable[(A, B)] with Map[A, B] with MapLike[A, B, Map[A, B]]
trait MapLike[A, +B, +This <: MapLike[A, B, This] with Map[A, B]]
extends MapLike[A, B, This]
trait MapLike[A, +B, +This <: MapLike[A, B, This] with Map[A, B]]
extends PartialFunction[A, B] with IterableLike[(A, B), This] with Subtractable[A, This]
trait IterableLike[+A, +Repr]
extends Equals with TraversableLike[A, Repr]
trait TraversableLike[+A, +Repr]
extends HasNewBuilder[A, Repr] with AnyRef
당신의 type 매개 변수를 전달하면 Map[Int, String]체인 아래로 모두에게 방법을, 우리는 유형에 전달 된 것을 발견 TraversableLike하고, 따라서에서 사용 map이다 :
A = (Int,String)
Repr = Map[Int, String]
예제로 돌아가서 첫 번째 맵은 type 함수를 수신 ((Int, String)) => (Int, Int)하고 두 번째 맵은 type 함수를 수신합니다 ((Int, String)) => String. 이중 괄호를 사용하여 튜플이 수신되고 있음을 강조했습니다 A.
그 정보로 다른 유형을 고려해 봅시다.
map Function.tupled(_ -> _.length):
B = (Int, Int)
map (_._2):
B = String
첫 번째에서 반환 된 유형 map은 Map[Int,Int]이고 두 번째는 Iterable[String]입니다. 보고 map의 정의, 이들의 값이라는 것을 쉽게 알 수있다 That. 그러나 그들은 어디에서 왔습니까?
관련된 클래스의 동반 객체를 살펴보면 암시 적 선언을 제공합니다. 대상 Map:
implicit def canBuildFrom [A, B] : CanBuildFrom[Map, (A, B), Map[A, B]]
그리고 객체 Iterable의 클래스는 다음과 같이 확장됩니다 Map.
implicit def canBuildFrom [A] : CanBuildFrom[Iterable, A, Iterable[A]]
이러한 정의는 매개 변수화 된 팩토리를 제공합니다 CanBuildFrom.
스칼라는 가능한 가장 구체적인 암시 적을 선택합니다. 첫 번째 경우는 첫 번째 CanBuildFrom였습니다. 두 번째 경우 첫 번째가 일치하지 않으므로 두 번째를 선택했습니다 CanBuildFrom.
질문으로 돌아 가기
유형의 유추 방법을 확인 하기 위해 질문 List및 map의 정의에 대한 코드를 다시 살펴 보겠습니다.
val map : Map[Int,String] = List("London", "Paris").map(x => (x.length, x))(breakOut)
sealed abstract class List[+A]
extends LinearSeq[A] with Product with GenericTraversableTemplate[A, List] with LinearSeqLike[A, List[A]]
trait LinearSeqLike[+A, +Repr <: LinearSeqLike[A, Repr]]
extends SeqLike[A, Repr]
trait SeqLike[+A, +Repr]
extends IterableLike[A, Repr]
trait IterableLike[+A, +Repr]
extends Equals with TraversableLike[A, Repr]
trait TraversableLike[+A, +Repr]
extends HasNewBuilder[A, Repr] with AnyRef
def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That
의 유형 List("London", "Paris")입니다 List[String]종류 때문에, A그리고 Repr에 대한 정의는 TraversableLike다음과 같습니다
A = String
Repr = List[String]
의 유형 (x => (x.length, x))은 (String) => (Int, String)이므로 유형 B은 다음과 같습니다.
B = (Int, String)
마지막으로 알려지지 않은 유형 That은의 결과 유형이며 map이미 그 유형도 있습니다.
val map : Map[Int,String] =
그래서,
That = Map[Int, String]
즉 breakOut, 유형 또는 하위 유형을 반드시 반환해야합니다 CanBuildFrom[List[String], (Int, String), Map[Int, String]].
List,하지만에map.