대답은 다음의 정의에 있습니다 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
.