26장 extractors - codeport/scala GitHub Wiki

##26.1 An example: extracting email address

  • 이메일 주소를 추출하는 예제

##26.2 Extractors

  • extractor는 unapply 메서드를 가지고 있는 객체다.
  • unapply메서드는 값을 매칭시켜서 분리하려는 목적이다.
  • unapply메서드는 생성과정인 apply의 반대이다.
  • unapply메서드는 파라미터가 원하는 객체가 아닌 경우도 처리해야 한다. 그래서 unapply메서드 Option타입을 리턴한다.
  • 패턴매칭이 extractor 객체를 만날 때마다 셀렉터표현식에서 extractor의 unapply메서드가 호출된다.
  • apply메서드는 아규먼트를 받아서 주어진 세트의 엘리먼트를 생성하므로 injection이라고 부르고 unapply메서드는 같은 세트의 엘리먼트를 받아서 엘리먼트의 일부를 추출하므로 extraction이라고 부른다.

##26.3 Patterns with zero or one variables

  • 익스트렉터 패턴은 어떤 변수에도 바인딩하지 않을 수도 있다. 이러한 경우에 unapplyboolean을 반환한다.

##26.4 Variable argument extractors

  • unapply메서드에서는 임의의 갯수의 아규먼트를 위해서 시퀀스 와일드카드 패턴인 _*를 사용할 수 없다. 성공한 경우에는 고정된 수의 엘리먼트를 반환하기 때문이다.

  • 이러한 경우를 위해서 다양한 아규먼트의 매칭(varang matching)을 위해서 unapplySeq를 정의하고 있다.

       def unapplySeq(whole: String): Option[Seq[String]] =
         Some(whole.split("\\.").reverse)
    
  • unapplySeq는 임의의 엘리먼트 타입 T를 가진 Option[Seq[T]]를 반환해야 한다.

##26.5 Extractors and sequence patterns

  • 표준 스칼라 라이브러리에서 시퀀스 패턴은 모두 extractors를 구현했다.

      List()
      List(x, y, _*)
      Array(x, 0, 0, _)
    
  • 다음과 같이 정의되어 있다.

      package scala
      object List {
        def apply[T](ellems: T*) = elems.toList
        def unapplySeq[T](x: List[T]): Option[Seq[T]] = Some(x)
        ...
      }
    

##26.6 Extractors versus case classes

  • case class는 단점이 하나 있는데 데이터의 고정된 표현을 노출한다. 즉, 생성자 패턴에서 클래스의 이름에 대응되는 셀렉터 객체의 고정된 표현 타입이 된다.
  • 익스트랙터는 데이터 표현과 패턴의 관계를 끊으므로 선택된 객체의 데이터 타입에는 아무것도 하지 않는다. 이를 representation independence라고 부른다. 대형 오픈시스템에서 representation independence가 중요한 이유는 컴포넌트의 클라이언트에 영향을 주지 않고 컴포넌트에서 구현타입을 바꿀수 있기 때문이다.
  • representation independence는 케이스클래스보다 익스트랙터가 좋은 장점중 하나다.
  • 반면에 케이스클래스도 익스트랙터보다 좋은 장점이 있다.
    1. 훨씬 쉽게 구성하고 정의할 수 있다. 필요한 코드가 더 적다.
    2. 익스트랙터보다 효율적인 패턴매칭을 한다. 컴파일러가 익스트랙터 패턴보다 더 잘 옵티마이징한다. 이는 unapplyupapplySeq가 무엇이든 할 수 있는데 반해서 케이스클래스의 메카니즘은 고정되어 있기 때문이다.
    3. 케이스클래스가 sealed 클래스를 상속받았다면 패턴매칭이 모든 경우의 수를 체크하는지 확인할 수 있다. 이는 익스트랙터에서는 할 수 없다.
  • 그럼 어느 것을 사용해야 하는가? 케바케다.
    • 닫힌 어플리케이션을 작성한다면 보통 케이스클래스가 더 났다. 더 간편하고 빠르고 정적 타입체킹을 하기 때문이다. 나중에 클래스 계층을 변경해야 한다면 리펙터링해야 하지만 별 문제는 아니다.
    • 알지못하는 클라이언트에 타입을 노출해야 한다면 익스트랙터가 더 낫다. representation independence를 유지할 수 있기 때문이다.
  • 다행히도 결정을 할 필요는 없다. 항상 케이스 클래스로 시작하고 필요한 경우에 익스트랩터로 바꿔라. 익스트랙터의 패텅과 케이스클래스의 패턴은 스칼라에서 완전히 같기 때문에 클라이언트의 패턴매칭은 여전히 동작할 것이다.

##26.7 Regular expressions

  • 스칼라의 정규표현식 문법은 Perl에서 가져온 자바의 문법을 따른다.

  • 스칼라의 졍규표현식 클래스는 scala.util.matching 패키지에 있다.

  • 정규표현식에서 이스케이프가 보기 실다면 """ """같은 로우스트링을 사요할 수 있다. .r을 분혀서 정규표현식을 얻을 수 있다.

      var Decimal = """(-)?(\d+)(\.\d*)?""".r
    
  • 스칼라의 모든 정규표현식은 익스트랙터를 정의하고 있다. 그래서 정규표현식의 그룹으로 매치된 서브스트링으로 추출된다.

      scala> val Decimal(sign, integerpart, decimalpart) = "-1,2,3"
      sign: String = -
      integerpart: String = 1
      decimalpart: String = .23