Scala Learning(2): map, flatMap, filter與For表達式

本文敘述Collections裏最常見的三種操作map, flatMap, filter,與For表達式的關係。

List對三種方法的實現

map在List的實現:

abstract class List[+T] {
  def map[U](f: T => U): List[U] = this match {
    case x :: xs => f(x) :: xs.map(f)
    case Nil => Nil
  }
}

flatMap在List的實現:

abstract class List[+T] {
  def flatMap[U](f: T => List[U]): List[U] = this match {
    case x :: xs => f(x) ++ xs.flatMap(f)
    case Nil => Nil
  }
}

filter在List的實現:

abstract class List[+T] {
  def filter(p: T => Boolean): List[T] = this match {
    case x :: xs =>
      if (p(x)) x :: xs.filter(p) else xs.filter(p)
    case Nil => Nil
  }
}

For表達式

下面這段邏輯,可以在for裏簡單表達:

(1 until n) flatMap (i =>
  (1 until i) filter (j => isPrime(i + j)) map
    (j => (i, j)))
for {
  i <- 1 until n
  j <- 1 until i
  if isPrime(i + j)
} yield (i, j)

for與map

for (x <- e1) yield e2

// translated to
e1.map(x => e2)

for與filter

for (x <- e1 if f; s) yield e2

// translated to
for (x <- e1.withFilter(x => f); s) yield e2

for與flatMap

for (x <- e1; y <- e2; s) yield e3

// translated to
e1.flatMap(x => for (y <- e2; s) yield e3)

for與Pattern Matching

結合前一篇文章(JSON表達)的例子,用for來做map、filter的操作:

val data: List[JSON] = ...
for {
  JObj(bindings) <- data
  JSeq(phones) = bindings("phoneNumbers")
  JObj(phone) <- phones
  JStr(digits) = phone("number")
  if digits startsWith "212"
} yield (bindings("firstName"), bindings("lastName"))

for裏面Pattern Matching的大致翻譯過程:

pat <- expr
// to
x <- expr withFilter {
    case pat => true
    case _ => false
  } map {
    case pat => x
  }

Exercise

for {
  x <- 2 to N
  y <- 2 to x
  if (x % y == 0)
} yield (x, y)

可以翻譯爲:

(2 to N) flatMap (x =>
  (2 to x) withFilter (y =>
    x % y == 0) map (y => (x, y)))

參考自 Principles of Reactive Programming

全文完 :)

發佈了158 篇原創文章 · 獲贊 25 · 訪問量 94萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章