복붙노트

[SCALA] 스칼라 2.8 브레이크 아웃

SCALA

스칼라 2.8 브레이크 아웃

스칼라 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)

무슨 일이야? 왜 브레이크 아웃은 내 목록에 인수로 호출되고?

해결법

  1. ==============================

    1.대답은지도의 정의에서 찾을 수 있습니다 :

    대답은지도의 정의에서 찾을 수 있습니다 :

    def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That 
    

    이 두 개의 매개 변수가 있습니다. 첫 번째는 함수이고 두 번째는 암시 적이다. 그 암시를 제공하지 않으면, 스칼라는 가장 구체적인 하나의 가능한 선택합니다.

    브레이크 아웃 소개

    그래서, 브레이크 아웃의 목적은 무엇인가? 질문에 주어진 예를 고려, 당신은 문자열 목록을 튜플 (INT, 문자열)로 각각의 문자열을 변환하고 그것에서지도를 생성합니다. 그렇게하는 가장 확실한 방법은 중간 목록 [(INT, 문자열)] 모음을 생성하고 변환합니다.

    지도는 결과 집합을 생성하는 빌더를 사용하는 감안할 때, 중간 목록을 생략하고지도에 직접 결과를 수집하는 것이 가능하지 않을까요? 분명히, 그래, 그것입니다. 이를 위해 지금, 그러나, 우리는 매핑 할 적절한 CanBuildFrom을 통과해야하고, 그 정확히 브레이크 아웃이하는 일이다.

    의는 브레이크 아웃의 정의에서, 다음을 보자 :

    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()
      }
    

    브레이크 아웃 매개 변수가 있음을, 그리고 그것을 CanBuildFrom의 인스턴스를 반환합니다. 공교롭게도 우리가지도를 기대 알고 있기 때문에, 종류 T,에서와 이미 추정 된 것으로 CanBuildFrom [목록 [문자열] (INT, 문자열), 맵 [INT, 문자열]]. 따라서:

    From = List[String]
    T = (Int, String)
    To = Map[Int, String]
    

    결론적으로의이 브레이크 아웃 자체받은 암시를 살펴 보자. 그것은 [아무것도, T에] 형 CanBuildFrom이다. 우리는 이미 이러한 모든 유형을 알고, 그래서 우리는 우리가 유형 CanBuildFrom [아무것도 (INT, 문자열), 맵 [INT, 문자열]]의 암시가 필요하다고 판단 할 수있다. 그러나 이러한 정의는 무엇입니까?

    의는 CanBuildFrom의 정의를 살펴 보자 :

    trait CanBuildFrom[-From, -Elem, +To] 
    extends AnyRef
    

    그래서 CanBuildFrom은 첫 번째 유형 매개 변수에 콘트라 변종이다. 아무것도 바닥 클래스가 없기 때문에 그 모든 클래스가 아무것도 대신에 사용될 수 있다는 것을 의미합니다 (즉, 그것은 모두의 서브 클래스입니다).

    이러한 빌더가 존재하기 때문에, 스칼라는 원하는 출력을 생성하는 데 사용할 수있다.

    빌더 소개

    스칼라 컬렉션 라이브러리 많은 방법은 (지도의 경우는 각 요소를 변형) 든 처리 원래 컬렉션을 고려하여 새로운 집합의 결과를 저장하는 구성.

    코드 재사용을 극대화하기 위해, 결과의 기억은 기본적으로 두 개의 동작 지원 작성기 (scala.collection.mutable.Builder)를 통해 수행된다 : 추가 요소 및 결과 집합을 반환. 이 결과 컬렉션의 유형은 빌더의 종류에 따라 달라집니다. 따라서, 목록 빌더는 목록을 반환하는지도 빌더 등지도를 반환하고 있습니다. 지도 방법의 구현은 결과의 유형 자체를 우려 할 필요는 없다 : 빌더가 처리한다.

    반면에, 그지도는 어떻게 든이 빌더를 수신 할 필요가 있다는 것을 의미한다. 설계 스칼라 2.8 컬렉션은 최고의 빌더가 가능한 선택하는 방법을 때 문제에 직면했다. (. _ 스왑) - 예를 들어, 내가 있다면지도 ( 'A'> 1) 쓰기 (-> ''1) 다시 .MAP, 나는지도를 좀하고 싶습니다. 반면에,지도 ( 'A'-> 1) .MAP (_._ 1)지도를 (그것이의 Iterable을 반환) 반환 할 수 없습니다.

    식의 알려진 유형의 최상의 빌더를 생산하는 마법 암시이 CanBuildFrom 통해 수행됩니다.

    CanBuildFrom 소개

    더 나은 무슨 일이 일어나고 있는지 설명하기 위해, 나는 매핑되는 컬렉션 대신 목록의지도입니다 예를 들어주지. 나중에 목록으로 돌아 갈 수 있습니다. 지금,이 두 표현을 고려 :

    Map(1 -> "one", 2 -> "two") map Function.tupled(_ -> _.length)
    Map(1 -> "one", 2 -> "two") map (_._2)
    

    첫 번째는지도와 두 번째 다시 표시의 Iterable을 반환합니다. 피팅 모음을 반환 마법 CanBuildFrom의 작품입니다. 현실을 이해하기 위해 다시지도의 정의를 살펴 보자.

    이 방법의지도는 TraversableLike에서 상속됩니다. 그것은 B와 그 나타내는 파라미터, 및 클래스를 파라미터 유형 매개 변수 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 어디에서 온의지도 자체의 정의를 살펴 보자, 이해하기 :

    trait Map[A, +B] 
    extends Iterable[(A, B)] with Map[A, B] with MapLike[A, B, Map[A, B]]
    

    TraversableLike이지도, A와에 repr를 확장하는 모든 특성을 상속되기 때문에 그 중 하나에서 상속 할 수있다. 마지막 하나는하지만, 기본 설정을 가져옵니다. 그래서, 불변의지도 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
    

    당신이지도 [INT, 문자열] 체인 끝까지의 type 매개 변수를 전달하면, 우리는 TraversableLike하고, 전달 유형 따라서,지도에서 사용하는, 것을 발견 :

    A = (Int,String)
    Repr = Map[Int, String]
    

    예로 돌아가서, 상기 제 맵 타입의 함수 (INT (문자열)) => (INT, int)를 수신하고, 상기 제 맵 타입의 함수 (INT (문자열)) => 문자열을 수신한다. 그게 우리가 본 같이의 종류이다, 그것은이 튜플이 수신되고 강조하기 위해 이중 괄호를 사용합니다.

    이 정보를 통해의 다른 유형을 살펴 보자.

    map Function.tupled(_ -> _.length):
    B = (Int, Int)
    
    map (_._2):
    B = String
    

    우리는 최초의지도에 의해 반환 유형이 맵 [INT, 지능이] 것을 볼 수 있고, 두 번째는의 Iterable [문자열]입니다. 지도의 정의를 살펴보면, 이러한 그건의 값이라는 것을 쉽게 알 수있다. 그러나 그들은 어디에서 오는가?

    우리가 포함 된 클래스의 동반자 객체의 내부를 보면, 우리는 몇 가지 암시 적 선언을 제공 참조하십시오. 객체지도 :

    implicit def  canBuildFrom [A, B] : CanBuildFrom[Map, (A, B), Map[A, B]]  
    

    그 수업지도에 의해 확장되고 객체의 Iterable :

    implicit def  canBuildFrom [A] : CanBuildFrom[Iterable, A, Iterable[A]]  
    

    이러한 정의는 매개 변수 CanBuildFrom을위한 공장을 제공합니다.

    스칼라는 가장 구체적인 암시 사용할 수를 선택합니다. 첫 번째 경우는 처음 CanBuildFrom이었다. 두 번째 경우, 일치하지 않는 첫 번째로, 제 2 CanBuildFrom를 선택했다.

    위로 질문에

    의 질문, 목록의과 (다시)지도의 정의는 유형을 유추하는 방법을 볼 수에 대한 코드를 보자 :

    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 
    

    목록의 유형 ( "런던", "파리") 목록 [문자열], 그래서 TraversableLike에 정의 된 유형 A와에 repr입니다 있습니다 :

    A = String
    Repr = List[String]
    

    (X => (x.length, X))의 유형 (문자열) => (INT 문자열)이므로 B의 종류 :

    B = (Int, String)
    

    마지막으로 알 수없는 유형, 즉지도의 결과의 유형입니다, 우리는 이미 그뿐만 아니라이 :

    val map : Map[Int,String] =
    

    그래서,

    That = Map[Int, String]
    

    즉, 브레이크 아웃, 반드시 CanBuildFrom의 유형 또는 하위 유형을 반환해야 함을 의미합니다 [목록 [문자열] (INT, 문자열), 맵 [INT, 문자열]].

  2. ==============================

    2.나는 다니엘의 대답에 구축하고 싶습니다. 그것은 매우 철저했지만, 코멘트에 언급 한 바와 같이, 그것은 브레이크 아웃이 무엇을 설명하지 않습니다.

    나는 다니엘의 대답에 구축하고 싶습니다. 그것은 매우 철저했지만, 코멘트에 언급 한 바와 같이, 그것은 브레이크 아웃이 무엇을 설명하지 않습니다.

    다시에서 촬영 : 명시 적 빌더 (2009-10-23)에 대한 지원, 여기에 내가 브레이크 아웃이하는 무엇을 믿는 것입니다 :

    그것은 컴파일러에게 Builder는 암시 적으로 선택하기로 제안을 제공합니다 (본질적으로는 컴파일러가이 상황에 가장 적합한 생각하는 공장을 선택할 수 있습니다.)

    예를 들어, 다음을 참조 :

    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)
    
  3. ==============================

    3.다니엘 소브랄의 대답은 큰이며, 스칼라 컬렉션의 아키텍처 (스칼라 프로그래밍의 25 장)과 함께 읽어야합니다.

    다니엘 소브랄의 대답은 큰이며, 스칼라 컬렉션의 아키텍처 (스칼라 프로그래밍의 25 장)과 함께 읽어야합니다.

    난 그냥 이유가 호출 브레이크 아웃에 정교하고 싶었 :

    우리가 한 종류에서 다른 침입하려는 때문에 :

    어떤 유형으로 어떤 유형의 탈옥? 예를 들어 서열에지도 기능을 볼 수 있습니다 :

    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]
    

    서열은 다른 서열을 구축하는 방법을 알고있는 이유는 (즉 암시 적 CanBuildFrom가 [서열 [_], B, 서열 [B]] 사용할 수 빌더 공장,하지만 서열지도하기에서 NO 빌더 공장이 없다).

    컴파일하기 위해, 우리는 형식 요건의 브레이크 아웃 어떻게 든해야하고,지도 기능을 사용하기위한지도를 생성하는 빌더를 구축 할 수 있습니다.

    다니엘은 설명했듯이, 브레이크 아웃은 다음 서명이 :

    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()
        }
    

    아무것도 모든 클래스의 서브 클래스입니다, 그래서 어떤 빌더 공장은 암시 나 대신에 대체 할 수 있습니다 : CanBuildFrom [아무것도, T에]. 우리는 브레이크 아웃 기능을 사용하는 경우 암시 적 매개 변수를 제공합니다 :

    val x: Map[String, Int] = Seq("A", "BB", "CCC").map(s => (s, s.length))(collection.breakOut)
    

    브레이크 아웃은 CanBuildFrom의 필수 유형 제공 할 수 있기 때문에, 컴파일 것 [서열 [(문자열, INT), (문자열, INT),지도 [문자열, 지능], 컴파일러는 암시 빌더 공장을 찾을 수있는 동안 형 CanBuildFrom의 [맵 [_, _, (A, B),지도 [A, B]], CanBuildFrom 대신 [아무것도, T에]에서, 브레이크 아웃은 실제 빌더를 만드는 데 사용하기 위해.

    CanBuildFrom [맵 [_, _] (A, B)는, 맵 [A가 B를] 맵에 정의하고, 단순히 기본지도를 사용하는 MapBuilder을 개시한다 있습니다.

    이 일을 지 웁니다 바랍니다.

  4. ==============================

    4.간단한 예는 브레이크 아웃이 무엇을하는지 이해하기 :

    간단한 예는 브레이크 아웃이 무엇을하는지 이해하기 :

    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]
    
  5. from https://stackoverflow.com/questions/1715681/scala-2-8-breakout by cc-by-sa and MIT license