Skip to content

6.8 순회하기, 연관시키기, 걸러내기, 접기, 축약하기

ik edited this page Sep 18, 2016 · 5 revisions

#6.8 순회하기, 연관시키기, 걸러내기, 접기, 축약하기

  • 일반적인 함수 컬렉션 (시퀀스, 리스트, 맵, 집합, 배열, 트리...)은 읽기 전용 순회를 기반으로 하는 여러가지 공통 연산을 지원한다.
  • 컨테이너 타입 구현 시 읽기 전용 순회 하는 식의 연산을 지원하면 균일성 갖을 수 있다 ( ex: Option )

###6.8.1 순회

trait IterableLike[+A, +Repr] extends Any with Equals with TraversableLike[A, Repr] with GenIterableLike[A, Repr] {
    def foreach[U](f: A => U): Unit =
        iterator.foreach(f)
  • foreach는 부수 효과만 수행할 수 있기 때문에 순수 함수가 아니지만 foreach를 사용해서 다른 순수 연산 ( map, filter, fold, reduce)을 구현할 수 있다.

###6.8.2 연관시키기

  • map은 원래 컬렉션과 같은 크기, 같은 종류의 새로운 컬렉션 반환
  • 새 컬렉션의 각 원소는 원래 컬렉션의 원소에 함수를 적용해서 변환한 결과
   def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = {
    def builder = { // extracted to keep method size under 35 bytes, so that it can be JIT-inlined
      val b = bf(repr)
      b.sizeHint(this) //원래 컬렉션과 같은 크기의 새로운 컬렉션
      b
    }
    val b = builder
    for (x <- this) b += f(x) //함수를 적용해서 변환한 결과
    b.result
  }
  • 2.7.x 에서 map 등의 메서드는 Iteratable이나 그와 비슷한 추상 객체를 반환 했고 실제로 Array 같은 저수준 타입이 반환 되었다.
  • CanBuildFrom을 사용해서 시그니처가 복잡해지긴 했지만 TraversableLike를 상속하는 Map, List 같은 컬렉션을 반활 할 수 있다. (재사용)
object Combinators {
  def map[A,B] (f:(A)=>B) (list:List[A]):List[B]= list map f
}

val intToString = (i:Int) => s"N=$i" //intToString: Int => String = <function1>

val flist = Combinators.map(intToString) _ //flist: List[Int] => List[String] = <function1>
  • f: A => B ---------> f: List[A] => List[B]와 같이 map을 사용해서 lift할 수 있다.
  • 스칼라 라이브러리의 일반적인 map은 인스턴스 메서드이기 때문에 lift 할 수 없다. -> OOP&FP로 인한 일종의 제약

###6.8.3 펼치면서 연관시키기

  • flat : 평평한, 고른
  def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That 
  def flatMap[B, That](f: A => GenTraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That 
  • flatMap == map + flatten
  • one depth만 펼친다.

###6.8.4 걸러내기

def filter(p: A => Boolean): Repr = filterImpl(p, isFlipped = false)

private def filterImpl(p: A => Boolean, isFlipped: Boolean): Repr = {
    val b = newBuilder
    for (x <- this)
      if (p(x) != isFlipped) b += x

    b.result
  }
  • TraversableLike 에는 필터링 하는 다양한 함수가 있는데 그중 일부는 무한 컬렉션을 반환하지 못하며, 일부는 순서가 보장되지 않는다.

###6.8.5 접기와 축약시키기

  • fold : "씨앗" 값으로 부터 시작
  def foldLeft[B](z: B)(op: (B, A) => B): B = {
    var result = z //씨앗값
    this foreach (x => result = op(result, x))
    result
  }
  • reduce : 원소 중 하나를 초깃값으로 사용
  def reduceLeft[B >: A](op: (B, A) => B): B = {
    if (isEmpty)
      throw new UnsupportedOperationException("empty.reduceLeft")

    var first = true
    var acc: B = 0.asInstanceOf[B]

    for (x <- self) {
      if (first) {
        acc = x // 초깃값
        first = false
      }
      else acc = op(acc, x)
    }
    acc
  }
  • foeach와 마찬가지로 모든 다른 연산을 fold로 구현 가능 -> source
  • 익명 함수에 전달 되는 원소에 주의
  • reduce, fold 는 일반적으로 left에 위임
  • fold는 씨앗값에 따라 완전 다른 타입을 반환 할 수 있지만 reduce는 항상 원소의 타입 또는 슈퍼 타입을 반환
  • 무한 컬렉션에 끝나지 않는다
  • 컬렉션에 순서가 보장 되지 않는 경우 결과가 달라질 수 있다
  • scan : 연속적인 부분집합에 대한 처리
Clone this wiki locally