스칼라 2.8 탈주
Scala 2.8 에는 다음과 같은 객체가 있습니다 scala.collection.package.scala
.
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()
}
이 결과는 다음과 같습니다.
> import scala.collection.breakOut
> val map : Map[Int,String] = List("London", "Paris").map(x => (x.length, x))(breakOut)
map: Map[Int,String] = Map(6 -> London, 5 -> Paris)
무슨 일이야? 왜 내 주장breakOut
에 부름 을 받고 List
있습니까?
대답은 다음의 정의에 있습니다 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]]
.
다니엘의 대답에 기초하고 싶습니다. 그것은 매우 철저했지만 주석에서 언급했듯이 브레이크 아웃이 무엇을하는지 설명하지 않습니다.
에서 촬영 재 : 명시 적 빌더에 대한 지원 (2009-10-23)는, 여기에 내가 브레이크 아웃이하는 무엇을 믿는 것입니다 :
컴파일러가 어떤 빌더를 내재적으로 선택할지에 대한 제안을 제공합니다 (본질적으로 컴파일러는 상황에 가장 적합한 공장을 선택할 수 있습니다).
예를 들어 다음을 참조하십시오.
scala> import scala.collection.generic._
import scala.collection.generic._
scala> import scala.collection._
import scala.collection._
scala> import scala.collection.mutable._
import scala.collection.mutable._
scala>
scala> 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: [From, T, To]
| (implicit b: scala.collection.generic.CanBuildFrom[Nothing,T,To])
| java.lang.Object with
| scala.collection.generic.CanBuildFrom[From,T,To]
scala> val l = List(1, 2, 3)
l: List[Int] = List(1, 2, 3)
scala> val imp = l.map(_ + 1)(breakOut)
imp: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 3, 4)
scala> val arr: Array[Int] = l.map(_ + 1)(breakOut)
imp: Array[Int] = Array(2, 3, 4)
scala> val stream: Stream[Int] = l.map(_ + 1)(breakOut)
stream: Stream[Int] = Stream(2, ?)
scala> val seq: Seq[Int] = l.map(_ + 1)(breakOut)
seq: scala.collection.mutable.Seq[Int] = ArrayBuffer(2, 3, 4)
scala> val set: Set[Int] = l.map(_ + 1)(breakOut)
seq: scala.collection.mutable.Set[Int] = Set(2, 4, 3)
scala> val hashSet: HashSet[Int] = l.map(_ + 1)(breakOut)
seq: scala.collection.mutable.HashSet[Int] = Set(2, 4, 3)
컴파일러가 반환 형식이 암시 적으로 선택되어 예상 형식과 가장 일치하는 것을 볼 수 있습니다. 수신 변수를 선언하는 방법에 따라 다른 결과가 나타납니다.
다음은 빌더를 지정하는 동등한 방법입니다. 이 경우 컴파일러는 빌더 유형에 따라 예상 유형을 유추합니다.
scala> def buildWith[From, T, To](b : Builder[T, To]) =
| new CanBuildFrom[From, T, To] {
| def apply(from: From) = b ; def apply() = b
| }
buildWith: [From, T, To]
| (b: scala.collection.mutable.Builder[T,To])
| java.lang.Object with
| scala.collection.generic.CanBuildFrom[From,T,To]
scala> val a = l.map(_ + 1)(buildWith(Array.newBuilder[Int]))
a: Array[Int] = Array(2, 3, 4)
Daniel Sobral의 답변은 훌륭하며 Scala Collections Architecture (Scala 의 프로그래밍 25 장) 와 함께 읽어야합니다 .
나는 그것이 왜 불리는 이유에 대해 자세히 설명하고 싶었습니다 breakOut
.
왜 그렇게 불려 breakOut
요?
우리 는 한 유형에서 다른 유형 으로 나누고 싶어하기 때문에 :
어떤 유형을 어떤 유형으로 나눕니 까? 상기 살펴 수 있습니다 map
에 기능을 Seq
예를 들어 :
Seq.map[B, That](f: (A) -> B)(implicit bf: CanBuildFrom[Seq[A], B, That]): That
다음과 같은 시퀀스 요소에 대한 매핑에서 직접 맵을 작성하려는 경우 :
val x: Map[String, Int] = Seq("A", "BB", "CCC").map(s => (s, s.length))
컴파일러는 다음과 같이 불평합니다.
error: type mismatch;
found : Seq[(String, Int)]
required: Map[String,Int]
Seq가 다른 Seq을 빌드하는 방법 만 알고 있기 때문입니다 (즉 CanBuildFrom[Seq[_], B, Seq[B]]
, 사용 가능한 암시 적 빌더 팩토리가 있지만 Seq에서 Map으로 빌더 팩토리 는 없습니다 ).
컴파일하려면, 우리는 어떻게 든 require breakOut
타입을 필요 로하고, map
함수가 사용할 맵을 생성하는 빌더를 구성 할 수 있어야 합니다.
Daniel이 설명했듯이 breakOut에는 다음과 같은 서명이 있습니다.
def breakOut[From, T, To](implicit b: CanBuildFrom[Nothing, T, To]): CanBuildFrom[From, T, To] =
// can't just return b because the argument to apply could be cast to From in b
new CanBuildFrom[From, T, To] {
def apply(from: From) = b.apply()
def apply() = b.apply()
}
Nothing
은 모든 클래스의 하위 클래스이므로 모든 빌더 팩토리를 대신 사용할 수 있습니다 implicit b: CanBuildFrom[Nothing, T, To]
. breakOut 함수를 사용하여 암시 적 매개 변수를 제공 한 경우 :
val x: Map[String, Int] = Seq("A", "BB", "CCC").map(s => (s, s.length))(collection.breakOut)
컴파일러는 breakOut
필요한 유형 의을 제공 할 수 있기 때문에 컴파일 CanBuildFrom[Seq[(String, Int)], (String, Int), Map[String, Int]]
하는 동안 breakOut이 실제 빌더를 작성하는 데 사용할 CanBuildFrom[Map[_, _], (A, B), Map[A, B]]
대신 암시 적 빌더 팩토리 유형 을 대신 CanBuildFrom[Nothing, T, To]
찾을 수 있습니다.
참고 CanBuildFrom[Map[_, _], (A, B), Map[A, B]]
지도에서 정의, 단순히 시작되고 MapBuilder
기본이되는지도를 사용하는을.
이것이 문제를 해결하기를 바랍니다.
무엇을 이해하는 간단한 예 breakOut
:
scala> import collection.breakOut
import collection.breakOut
scala> val set = Set(1, 2, 3, 4)
set: scala.collection.immutable.Set[Int] = Set(1, 2, 3, 4)
scala> set.map(_ % 2)
res0: scala.collection.immutable.Set[Int] = Set(1, 0)
scala> val seq:Seq[Int] = set.map(_ % 2)(breakOut)
seq: Seq[Int] = Vector(1, 0, 1, 0) // map created a Seq[Int] instead of the default Set[Int]
참고 URL : https://stackoverflow.com/questions/1715681/scala-2-8-breakout
'IT story' 카테고리의 다른 글
npm 설치는 devDependencies를 설치하지 않습니다 (0) | 2020.04.20 |
---|---|
레일스 매개 변수 설명? (0) | 2020.04.20 |
절차 적 프로그래밍과 기능적 프로그래밍의 차이점은 무엇입니까? (0) | 2020.04.20 |
SQL Server Management Studio 2012는 어디에 있습니까? (0) | 2020.04.20 |
고정 헤더가있는 HTML 테이블? (0) | 2020.04.20 |