🔧
高階関数でコード重複を半分以下に!Scala 2 REPLハンズオン講座
— 実行しながら 重複除去テクニック を体感する
0. REPL を開く
scala
1. “コピペ量産”版 V1
import java.io.File
object FileMatcherV1 {
private val filesHere = new File(".").listFiles
def filesEnding(q: String) = for (f <- filesHere; if f.getName.endsWith(q)) yield f
def filesContaining(q: String) = for (f <- filesHere; if f.getName.contains(q)) yield f
def filesRegex(q: String) = for (f <- filesHere; if f.getName.matches(q)) yield f
}
実行例
scala> FileMatcherV1.filesEnding(".scala").map(_.getName).toList
val res0: List[String] = List(b.scala, scala.scala, c.scala, a.scala)
scala> FileMatcherV1.filesContaining("c").map(_.getName).toList
val res1: List[String] = List(b.scala, scala.scala, c.scala, a.scala)
2. 高階関数で共通化 V2
import java.io.File
object FileMatcherV2 {
private val filesHere = new File(".").listFiles
private def filesMatching(q: String,
matcher: (String, String) => Boolean) =
for (f <- filesHere; if matcher(f.getName, q)) yield f
def filesEnding(q: String) = filesMatching(q, _.endsWith(_))
def filesContaining(q: String) = filesMatching(q, _.contains(_))
def filesRegex(q: String) = filesMatching(q, _.matches(_))
}
実行例
scala> FileMatcherV2.filesRegex("a\\.scala").map(_.getName)
val res2: Array[String] = Array(a.scala)
付録 — カリー化 & クロージャでもっと短く
Scala らしい書き方として参考までに
import java.io.File
object FileMatcherCurried {
private val filesHere = new File(".").listFiles
private def filesMatching(q: String) // ← 第1引数リスト
(matcher: String => Boolean) = // ← 第2引数リスト
for (f <- filesHere; if matcher(f.getName)) yield f
def filesEnding(q: String) = filesMatching(q)(_.endsWith(q))
def filesContaining(q: String) = filesMatching(q)(_.contains(q))
def filesRegex(q: String) = filesMatching(q)(_.matches(q))
}
実行例
scala> FileMatcherCurried.filesContaining("b").map(_.getName)
val res3: Array[String] = Array(b.scala)
🔹 カリー化(Currying)とは?
* 複数の引数リストに分けるテクニック
def f(a)(b) = ... と書くと
f(a) の時点で (b) => … な 部分適用関数が返る。
🔹 クロージャとは?
* 関数値が スコープ外の変数を捕捉(持ち歩き)できる性質。
ここでは q を閉じ込めた _.endsWith(q) などがクロージャ。
おわりに ☕️
1. 似た処理はまず高階関数 (V2)
2. さらに カリー化+クロージャで API 化 (付録A)
3. 行数 33 → 14、重複 for は 3→1 のスッキリ感を体験しよう!
Scala REPL だけで動かせるので、ぜひ手を動かしてみてください。
Happy Scala Hacking! 🐾
参考文献
- マーティン・オーダースキー, ほか.
『Scala スケーラブルプログラミング[第4版]』
第9章「制御構造の抽象化」9.1節 コードの重複の削減.
オライリー・ジャパン, 2021.
Discussion