Posted at

Scalaの遅延評価(lazy)を関数オブジェクトで使ってみる


概要

Scalaでよく使う遅延評価(lazy)を、関数オブジェクトに適用して、処理効率を上げてみたときの内容をサンプルコードにまとめます。なお、遅延評価の詳細についてはプログラミング言語Scala 日本語情報サイト 第 14 章 遅延評価valを参照ください。


例題

以下が処理効率化する前のサンプルコードです。partitionの中で本来一度で済むはずのOptionの空判定やSeqのlengthメソッドの呼び出しを、ループ回数分だけ行っています。なお、partitionについては@f81@githubさんの第10章:Scalaの「Traversable」リファレンスを参照ください。


Before.scala

  def testBefore(judgeOpt: Option[Int]) = {

val numberList = Seq(1,2,3,4,5,6,7,8,9,10)

val (leftList,rightList) = numberList.partition{ n =>
if (judgeOpt.isEmpty) {
false
} else if (judgeOpt.get > numberList.length) {
true
} else {
if (n % judgeOpt.get == 0) {
false
} else {
true
}
}
}
println(leftList)
}



lazyを使ったサンプルコード

対応として1つ目と2つ目のブロックをあらかじめBooleanで評価するという手もありますが、今回はlazyを用いて処理の効率化を実現してみます。

下記のサンプルコードの通り、関数オブジェクトを遅延評価する関数(judgeFunction)を作成しpartition内で使用する判定関数を一度の評価で決定します。なお、judgeFunction中の「println("None")」や「println("Over")」は一度しか処理されません。


After.scala

  def testAfter(judgeOpt: Option[Int]) = {

val numberList = Seq(1,2,3,4,5,6,7,8,9,10)
def judgeFunction(judgeOpt: Option[Int], list: Seq[Int]):(Int) => Boolean = {
if (judgeOpt.isEmpty) {
println("None")
(_: Int) => false
} else if (judgeOpt.get > list.length) {
println("Over")
(_: Int) => true
} else {
divideFuncton(_: Int, judgeOpt.get)
}
}
def divideFuncton(target: Int, input: Int):Boolean = {
if (target % input == 0) {
false
} else {
true
}
}

lazy val f = judgeFunction(judgeOpt, numberList)
val (leftList,rightList) = numberList.partition{ n =>
f(n)
}
println(leftList)
}