π Chapter 3: ν¨μμ μλ£κ΅¬μ‘°
π ν¨μμ μλ£κ΅¬μ‘°μ μλ―Έβ
ν¨μμ μλ£κ΅¬μ‘°λ μ€μ§ μμ ν¨μλ§μΌλ‘ μ‘°μλλ μλ£κ΅¬μ‘°μ΄λ€. μμ ν¨μλ μλ£λ₯Ό κ·Έ μ리μμ λ³κ²½νκ±°λ κΈ°ν λΆμ ν¨κ³Όλ₯Ό μννλ μΌμ΄ μμ΄μΌ ν¨μ κΈ°μ΅νκΈ° λ°λλ€. λ°λΌμ ν¨μμ μλ£κ΅¬μ‘°λ μ μμ μν΄ λΆλ³μ΄λ€.
κ°μ₯ 보λ³μ μΈ ν¨μμ μλ£κ΅¬μ‘°λΌ ν μ μλ λ¨μΌ μ°κ²° λͺ©λ‘μ μ΄ν΄λ³΄μ. λ€μμ λ¨μΌ μ°κ²° λͺ©λ‘ μ μλ μ¬μ€μ μ€μΉΌλΌμ νμ€ λΌμ΄λΈλ¬λ¦¬μ μ μλμ΄ μλ List
μλ£ νμμ κ²κ³Ό κ°λ
μ μΌλ‘ λμΌνλ€.
package fpinscala.datastructures
sealed trait List[+A]
case object Nil extends List[Nothing]
case class Cons[+A](head: A, tail: List[A]) extends List[A]
object List {
def sum(ints: List[Int]): Int = ints match {
case Nil => 0
case Cons(x, xs) => x + sum(xs)
}
def product(ds: LIst[Double]): Double = ds match {
case Nil => 1.0
case Cons(0.0, _) => 0.0
case Cons(x, xs) => x * product(xs)
}
def apply[A](as: A*): List[A] =
if (as.isEmpty) Nil
else Cons(as.head, apply(as.tail: _*))
}
μΌλ°μ μΌλ‘ μλ£ νμμ λμ
ν λλ trait
ν€μλλ₯Ό μ¬μ©νλ€. trait
ν€μλλ‘ μ μνλ νΉμ§(trait)μ νλμ μΆμ μΈν°νμ΄μ€λ‘, νμνλ€λ©΄ μΌλΆ λ©μλμ ꡬνμ λ΄μ μλ μλ€. μ§κΈ μμμλ trait
λ₯Ό μ΄μ©ν΄μ List
λΌλ νΉμ§μ μ μνλ€. μ΄ νΉμ§μλ λ©μλκ° νλλ μλ€. trait
μμ sealed
λ₯Ό λΆμ΄λ κ²μ μ΄ νΉμ§μ λͺ¨λ ꡬνμ΄ λ°λμ μ΄ νμΌ μμ μ μΈλμ΄ μμ΄μΌ ν¨μ λ»νλ€.
κ·Έ λ€μμλ μλ case
ν€μλλ‘ μμνλ λ μ€μ List
μ λ κ°μ§ ꡬν, μ¦ λ κ°μ§ μλ£ μμ±μμ΄λ€. List
λ μλ£ μμ±μ Nil
λ‘ νκΈ°λλ λΉ λͺ©λ‘μΌ μλ μκ³ μλ£ μμ±μ Cons
(κ΄λ‘μ constructorμ μ€μλ§λ‘ μ°μΈλ€)λ‘ νκΈ°λλ λΉμ§ μμ λͺ©λ‘μΌ μλ μλ€. λΉμ§ μμ λͺ©λ‘μ μ΄κΈ° μμ head
λ€μμ λλ¨Έμ§ μμλ€μ λ΄μ νλμ List
λ‘ κ΅¬μ±λλ€.
case object Nil extends List[Noting]
case class Cons[+A](head: A, tail: List[A]) extends List[A]
ν¨μλ₯Ό λ€νμ μΌλ‘ λ§λ€ μ μλ―μ΄, μλ£ νμλ λ€νμ μΌλ‘ λ§λ€ μ μλ€. sealed trait List
λ€μμ νμ 맀κ°λ³μ [+A]
λ₯Ό λκ³ Cons
μλ£ μμ±μ μμμ κ·Έ A
맀κ°λ³μλ₯Ό μ¬μ©ν¨μΌλ‘μ¨, λͺ©λ‘μ λ΄κΈ΄ μμλ€μ νμμ λν΄ λ€νμ±μ΄ μ μ©λλ List
μλ£ νμμ΄ λ§λ€μ΄μ§λ€. κ·Έλ¬λ©΄ νλμ λμΌν μ μλ‘ Int
μμλ€μ λͺ©λ‘, Double
μμλ€μ λͺ©λ‘, String
μμλ€μ λͺ©λ‘ λ±λ±μ μ¬μ©ν μ μκ² λλ€(νμ λ§€κ° λ³μ A
μ μλ +
λ 곡λ³μ λ»νλ€).
μλ£ μμ±μ μ μΈμ ν΄λΉ ννμ μλ£ νμμ ꡬμΆνλ ν¨μλ₯Ό λμ νλ€. λ€μμ μλ£ μμ±μμ μ©λ λͺ κ°μ§μ΄λ€.
val ex1: List[Double] = Nil
val ex2: List[Int] = Cons(1, Nil)
val ex3: List[String] = Cons("a", Cons("b", Nil))
case object Nil
μ μν΄ Nil
μ΄λΌλ νκΈ°λ₯Ό μ΄μ©ν΄μ λΉ List
λ₯Ό ꡬμΆν μ μκ² λλ©°, case class Cons
λ Cons(1, Nil), Cons("a", Cons("b", Nil))
κ°μ νκΈ°λ₯Ό ν΅ν΄μ μμμ κΈΈμ΄μ λ¨μΌ μ°κ²° λͺ©λ‘μ ꡬμΆν μ μκ² νλ€. List
λ νμ A
μ λν΄ λ§€κ°λ³μνλμ΄ μμΌλ―λ‘, μ΄ ν¨μλ€ μμ μλ‘ λ€λ₯Έ νμμ A
μ λν΄ μΈμ€ν΄μ€νλλ λ€νμ ν¨μλ€μ΄λ€.
κ° μλ£ μμ±μλ sum
μ΄λ product
κ°μ ν¨μλ€μμμ²λΌ ν¨ν΄ λΆν©(pattern matching)μ μ¬μ©ν μ μλ ν¨ν΄λ λμ
νλ€.
π ν¨ν΄ λΆν©β
object List
μ μν ν¨μ sum
κ³Ό product
λ₯Ό μμΈν μ΄ν΄λ³΄μ. μ΄λ° ν¨μλ€μ List
μ λλ° κ°μ²΄
(companion object)λΌκ³ λΆλ₯΄κΈ°λ νλ€. λ ν¨μ λͺ¨λ ν¨ν΄ λΆν©μ νμ©νλ€.
def sum(ints: List[Int]): Int = ints match {
case Nil => 0
case Cons(x, xs) => x + sum(xs)
}
def product(ds: LIst[Double]): Double = ds match {
case Nil => 1.0
case Cons(0.0, _) => 0.0
case Cons(x, xs) => x * product(xs)
}
μ΄λ€μ΄ μ¬κ·μ μΈ μ μμμ μ£Όλͺ©νκΈ° λ°λλ€. List
κ°μ μ¬κ·μ μΈ μλ£ νμμ λ€λ£¨λ ν¨μλ€μ μμ±ν λλ μ΄μ²λΌ μ¬κ·μ μΈ μ μλ₯Ό μ¬μ©νλ κ²½μ°κ° λ§λ€.
ν¨ν΄ λΆν©μ ννμμ ꡬ쑰λ₯Ό λ°λΌ λ΄λ €κ°λ©΄μ κ·Έ ꡬ쑰μ λΆλΆ ννμμ μΆμΆνλ 볡μ‘ν switch
λ¬Έκ³Ό λΉμ·νκ² μλνλ€. λΆν©μ κ° κ²½μ° λ¬Έμ =>
μ μ’λ³μ ν¨ν΄μ΄ μκ³ κ·Έ μ°λ³μ κ²°κ³Όκ° μλ ννμ΄λ€. λμμ κ²½μ° λ¬Έμ ν¨ν΄κ³Ό λΆν©νλ©΄, κ·Έ κ²½μ° λ¬Έμ κ²°κ³Όκ° μ 체 λΆν© ννμμ κ²°κ³Όκ° λλ€. λ§μΌ λμκ³Ό λΆν©νλ ν¨ν΄μ΄ μ¬λ¬ κ°λ©΄ μ€μΉΌλΌλ μ²μμΌλ‘ λΆν©νλ κ²½μ° λ¬Έμ μ ννλ€.
π ν¨μμ μλ£κ΅¬μ‘°μ μλ£ κ³΅μ β
μλ£κ° λΆλ³μ΄λΌλ©΄, μλ₯Ό λ€μ΄ λͺ©λ‘μ μμλ₯Ό μΆκ°νκ±°λ λͺ©λ‘μμ μμλ₯Ό μ κ±°νλ ν¨μλ μ΄λ»κ² μμ±ν΄μΌ ν κΉ? κΈ°μ‘΄ λͺ©λ‘μ μμ 1μ΄λΌλ μμ λ₯Ό μΆκ°νλ €λ©΄ Cons(1, xs)
λΌλ μ λͺ©λ‘μ λ§λ€λ©΄ λλ€. λͺ©λ‘μ λΆλ³μ΄λ―λ‘ xs
λ₯Ό μ€μ λ‘ λ³΅μ¬ν νμλ μλ€. κ·Έλ₯ μ¬μ¬μ©νλ©΄ λλ€. μ΄λ₯Ό μλ£ κ³΅μ λΌκ³ λΆλ₯Έλ€. λΆλ³μ΄ μλ£λ₯Ό 곡μ νλ©΄ ν¨μλ₯Ό μ’ λ ν¨μ¨μ μΌλ‘ ꡬνν μ μμ λκ° λ§λ€. μλ£κ° λ³νκ±°λ κΉ¨μ§μ§ μλλ‘ λ°©μ΄μ μΌλ‘ 볡μ¬λ³Έμ λ§λ€μ΄ λ νμκ° μλ κ²μ΄λ€.
μ΄λ₯Ό λκ³ ν¨μμ μλ£κ΅¬μ‘°λ μμμ (persistent)μ΄λΌκ³ λ§νλ€. μ΄λ μλ£κ΅¬μ‘°μ μ°μ°μ΄ κ°ν΄μ Έλ κΈ°μ‘΄μ μ°Έμ‘°λ€μ΄ κ²°μ½ λ³νμ§ μμμ λ»νλ€.
π μλ£ κ³΅μ μ ν¨μ¨μ±β
μλ£ κ³΅μ λ₯Ό μ΄μ©νλ©΄ μ°μ°μ μ’ λ ν¨μ¨μ μΌλ‘ μνν μ μλ κ²½μ°κ° λ§λ€. μλ£ κ³΅μ μ λμ± λλΌλ μλ‘, λ€μ ν¨μλ ν λͺ©λ‘μ λͺ¨λ μμλ₯Ό λ€λ₯Έ λͺ©λ‘μ λμ μΆκ°νλ€.
def append[A](a1: List[A], a2: List[A]): List[A] =
a1 match {
case Nil => a2
case Cons(h, t) => Cons(h, append(t, a2))
}
μ΄ μ μλ μ€μ§ 첫 λͺ©λ‘μ΄ λ€ μμ§λ λκΉμ§λ§ κ°λ€μ 볡μ¬ν¨μ μ£ΌμνκΈ° λ°λλ€. λ°λΌμ μ΄ ν¨μμ μ€ν μκ°κ³Ό λ©λͺ¨λ¦¬ μ¬μ©λμ μ€μ§ a1
μ κΈΈμ΄μλ§ μμ‘΄νλ€. λͺ©λ‘μ λλ¨Έμ§λ κ·Έλ₯ a2
λ₯Ό κ°λ¦¬ν¬ λΏμ΄λ€. λ§μΌ μ΄ ν¨μλ₯Ό λ°°μ΄ λ κ°λ₯Ό μ΄μ©ν΄μ ꡬννλ€λ©΄ λ λ°°μ΄μ λͺ¨λ μμλ₯Ό κ²°κ³Ό λ°°μ΄μ 볡μ¬ν΄μΌ νμ κ²μ΄λ€. μ΄ κ²½μ° λΆλ³μ΄ μ°κ²° λͺ©λ‘μ΄ λ°°μ΄λ³΄λ€ ν¨μ¬ ν¨μ¨μ μ΄λ€.
λ¨μΌ μ°κ²° λͺ©λ‘μ ꡬ쑰 λλ¬Έμ, Cons
μ tail
μ μΉνν λλ§λ€ λ°λμ μ΄μ μ λͺ¨λ Cons
κ°μ²΄λ₯Ό 볡μ¬ν΄μΌ νλ€. μλ‘ λ€λ₯Έ μ°μ°λ€μ ν¨μ¨μ μΌλ‘ μ§μνλ μμ ν¨μμ μλ£κ΅¬μ‘°λ₯Ό μμ±ν λμ κ΄κ±΄μ μλ£ κ³΅μ λ₯Ό νλͺ
νκ² νμ©νλ λ°©λ²μ μ°Ύμλ΄λ κ²μ΄λ€. (μ€μΉΌλΌ νμ€ λΌμ΄λΈλ¬λ¦¬μ μμ ν¨μμ μμ°¨μ΄ κ΅¬νμΈ Vectorλ₯Ό μ°Έκ³ )
π κ³ μ°¨ ν¨μλ₯Ό μν νμ μΆλ‘ κ°μ β
dropWhile
κ°μ κ³ μ°¨ ν¨μμλ νν μ΅λͺ
ν¨μλ₯Ό λ겨μ€λ€. κ·ΈλΌ μ νμ μΈ μλ₯Ό νλ 보μ. μ°μ dropWhile
μ μλͺ
μ λ μ¬λ €λ³΄μ.
def dropWhile[A](l: List[A], f: A => Boolean): List[A]
μΈμ f
μ μ΅λͺ
ν¨μλ₯Ό μ§μ ν΄μ νΈμΆνκΈ° μν΄μλ κ·Έ μ΅λͺ
ν¨μμ μΈμμ νμμ λͺ
μν΄μΌ νλ€.
val xs: List[int] = List(1,2,3,4,5)
val ex1 = dropWhile(xs, (x: Int) => x < 4)
// List(4,5)
x
μ νμμ΄ Int
μμ λͺ
μμ μΌλ‘ νκΈ°ν΄μΌ νλ€λ κ²μ λ€μ λ²κ±°λ‘λ€. dropWhile
μ 첫 μΈμλ List[Int]
μ΄λ―λ‘, λμ§Έ μΈμμ ν¨μλ λ°λμ Int
λ₯Ό λ°μμΌ νλ€. λ€μμ²λΌ dropWhile
μ μΈμλ€μ λ κ·Έλ£ΉμΌλ‘ λ¬ΆμΌλ©΄ μ€μΉΌλΌκ° κ·Έλ¬ν μ¬μ€μ μΆλ‘ ν μ μλ€.
def dropWhile[A](as: List[A])(f: A => Boolean): List[A] =
as match {
case Cons(h, t) if f(h) => dropWhile(t)(f)
case _ => as
}
μ΄ λ²μ μ dropWhile
μ νΈμΆνλ ꡬ문μ dropWhile(xs)(f)
μ ννμ΄λ€. μ¦, dropWhile(xs)
λ νλμ ν¨μλ₯Ό λλ €μ£Όλ©°, κ·Έ ν¨μλ₯Ό μΈμ f
λ‘ νΈμΆνλ€. μΈμλ€μ μ΄λ° μμΌλ‘ λ¬Άλ κ²μ μ€μΉΌλΌμ νμ μΆλ‘ μ λκΈ° μν κ²μ΄λ€. μ΄μ λ dropWhile
μ λ€μκ³Ό κ°μ΄ νμ μ£Όν΄ μμ΄ νΈμΆν μ μλ€.
val xs: List[Int] = List(1,2,3,4,5)
val ex1 = dropWhile(xs)(x => x < 4)
x
μ νμμ λͺ
μμ μΌλ‘ μ§μ νμ§ μμμμ μ£Όλͺ©νκΈ° λ°λλ€.
μ’ λ μΌλ°ννμλ©΄, ν¨μ μ μμ μ¬λ¬ κ°μ μΈμ κ·Έλ£Ήμ΄ μ‘΄μ¬νλ κ²½μ° νμ μ 보λ κ·Έ μΈμ κ·Έλ£Ήλ€μ λ°λΌ μΌμͺ½μμ μ€λ₯Έμͺ½μΌλ‘ νλ¬κ°λ€. μ΄μ²λΌ, νμ μΆλ‘ μ΄ μ΅λλ‘ μΌμ΄λλλ‘ ν¨μ μΈμλ€μ μ μ ν μμμ μ¬λ¬ μΈμ λͺ©λ‘μ λ¬Άλ κ²½μ°κ° λ§λ€.
π λͺ©λ‘μ λν μ¬κ·μ κ³ μ°¨ ν¨μλ‘μ μΌλ°νβ
sum
κ³Ό product
μ ꡬνμ λ€μ μ΄ν΄λ³΄μ.
def sum(ints: List[Int]): Int = ints match {
case Nil => 0
case Cons(x, xs) => x + sum(xs)
}
def product(ds: LIst[Double]): Double = ds match {
case Nil => 1.0
case Cons(x, xs) => x * product(xs)
}
λ μ μκ° μμ£Ό λΉμ·νλ€λ μ μ μ£Όλͺ©νμ. μ΄λ° μ½λ μ€λ³΅μ λ°κ²¬νλ€λ©΄ λΆλΆ ννμλ€μ μΆμΆν΄μ ν¨μ μΈμλ‘ λ체ν¨μΌλ‘μ¨ μ½λλ₯Ό μΌλ°ννλ κ²μ΄ νμ κ°λ₯νλ€. μΌλ°νλ ν¨μλ λΉ λͺ©λ‘μΌ λμ λ°νκ°κ³Ό λΉμ§ μμ λͺ©λ‘μΌ λ κ²°κ³Όμ μμλ₯Ό μΆκ°νλ ν¨μλ₯Ό μΈμλ‘ λ°λλ€.
def foldRight[A, B](as: List[A], z: B)(f: (A, B) => B): B =
as match{
case Nil => z
case Cons(x, xs) => f(x, foldRight(xs, z)(f))
}
def sum2(ns: List[Int]) =
foldRight(ns, 0)((x,y) => x + y)
def product2(ns: List[Double]) =
foldRight(ns, 1.0)(_ * _) // (x, y) => x * yλ₯Ό μ’ λ κ°κ²°νκ² νκΈ°ν κ²μ΄λ€.
foldRight
λ νλμ μμ νμμλ§ νΉνλμ§ μμλ€. κ·Έλ¦¬κ³ μΌλ°ν κ³Όμ μμ μ°λ¦¬λ μ΄ ν¨μκ° λλ €μ£Όλ κ°μ΄ λͺ©λ‘μ μμμ κ°μ νμμΌ νμκ° μλ€λ μ λ μκ² λμλ€. foldRight
κ° νλ μΌμ μ΄λ° μμΌλ‘ μ€λͺ
ν μ μλ€: μ΄ ν¨μλ λ€μμμ 보λ―μ΄ λͺ©λ‘μ μμ±μ Nil
κ³Ό Cons
λ₯Ό z
μ f
λ‘ μΉννλ€.
Cons(1, Cons(2, Nil))
f(1, f(2, z))
foldRight
κ° νλμ κ°μΌλ‘ μΆμ½λλ €λ©΄ λ°λμ λͺ©λ‘μ λκΉμ§ μνν΄μΌ ν¨μ μ£Όλͺ©νκΈ° λ°λλ€.
π κ·Έ μΈμ λͺ©λ‘ μ‘°μ ν¨μλ€β
λͺ©λ‘μ μ²λ¦¬νλ ν¨μλ₯Ό μμ±ν λλ λͺ μμ μΈ μ¬κ· ν¨μλ₯Ό μΌλ°ννλ μ¬λ¬ λ°©λ²μ κ³ λ €ν΄ λ³΄λ μ΅κ΄μ κ°μ§κΈΈ λ°λλ€. κ·Έλ κ² νλ©΄ μ΄λ° ν¨μλ€μ μ€μ€λ‘ (μ¬)λ°κ²¬νκ² λ κ²μ΄λ©°, κ·Έλ¬λ€ 보면 κ°κ°μ μ μ ν μ©λλ₯Ό λ³Έλ₯μ μΌλ‘ μκ² λ κ²μ΄λ€.