-
Notifications
You must be signed in to change notification settings - Fork 7
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 : 연속적인 부분집합에 대한 처리