今回は前に自分がなかなか読めなくて苦戦したScalaの機能、foldLeftについて書いてみたいと思う。
foldLeftとは
foldLeftについて調べるとアキュームレータだの、再帰だの難しい話がたくさん出てきてイメージがつきにくいので、この記事ではイメージすることをメインに書きたいと思う。
Scalaでは以前の記事で書いたforeachのように、処理をループさせてリストを整形していくことが多々あるのだが、同じような仲間にfoldLeftというものがある。
リファレンスの定義にはこのように書いてある。
def foldLeft[B](z: B)(op: (B, A) ⇒ B): B
これだけみても訳がわからないが、この定義のポイントはカリー化された1つ目の引数と戻り値が同じBであるということ。
Leftをfoldする
個人的にはholdLeftという関数名の方がしっくりくるのだが、左側(1つ目の引数)で渡されたものは保持したまま、2個目で渡された引数の処理(op: (B, A) ⇒ B)を要素分繰り返すイメージ。
以前の記事から神々の名前の入ったListを転用してサンプルを書いてみる。
val strArray = List("Anubis", "Isis", "Osiris", "Geb", "Atum")
いつも通り、この中から頭文字が”A”の名前だけ抜き出したいとする。
ただ今回は抜き出すだけではなく、頭文字がAの名前とそれ以外の名前を振り分けて新たにリストを作りたい。
そんなときにこのfoldLeftを使うことでサクッと処理ができる。
foldLeftを利用してリストを作り直す
では実際にfoldLeftを使ってみる。
val (a_array, o_array) = strArray.foldLeft((List.empty[String], List.empty[String]))((x1, x2) => { x2.startsWith("A") match { case true => ((x1._1 :+ x2), x1._2) case _ => (x1._1, (x1._2 :+ x2)) } })
カリー化された1つ目の引数には空のリストを2つtupleに格納し渡している。2つ目の引数にはラムダを渡している。
このラムダは引数に(x1, x2)を受け取り、前回の記事でも出てきたmatch-caseで処理を振り分けている。
で、このx1とx2には何が入ってくるのかという話だが、x1には1つ目の引数で渡されたtupleが入ってくる。そしてx2にはstrArrayの要素が順番に格納されてくる。
なので、その渡されてきた文字列の頭文字が”A”であるかの判定を行い、その結果がtrueだった場合はtupleの1つ目の要素に:+(add)し、2つ目の要素はそのまま返すという処理を行う。
それ以外の場合は、1つ目の要素はそのままに、2つ目の要素に:+を行い返す。
そして最終的な結果が(a_array, o_array)のtupleに格納される。
中身を確認するために出力のコードも加え、実行してみる。
val strArray = List("Anubis", "Isis", "Osiris", "Geb", "Atum") val (a_array, o_array) = strArray.foldLeft((List.empty[String], List.empty[String]))((x1, x2) => { x2.startsWith("A") match { case true => ((x1._1 :+ x2), x1._2) case _ => (x1._1, (x1._2 :+ x2)) } }) a_array.foreach(println) println("--------------") o_array.foreach(println)
実行結果は以下
username$ scala foldleft.scala Anubis Atum -------------- Isis Osiris Geb
こんな感じになる。
突然tupleとかも当たり前のようにミックスして来たが、今回は許してほしい。
チームでScalaを使うときが来たら補足するかもしれない。
コメント