Scala 関数引数の遅延評価

個人開発したアプリの宣伝
目的地が設定できる手帳のような使い心地のTODOアプリを公開しています。
Todo with Location

Todo with Location

  • Yoshiko Ichikawa
  • Productivity
  • Free

スポンサードリンク

式評価のskip、実行について

式評価の基準について。

// falseで条件外となるので{println("!!"); true}は評価されない
scala> false && { println("!!"); true }
res1: Boolean = false

// {println("!!"); true}が評価され、式結果はtrueとなる
scala> true && { println("!!"); true }
!!
res2: Boolean = true

// !!は出力されるが、true && falseが評価され、式結果はfalseとなる
scala> true && { println("!!"); false }
!!
res3: Boolean = false

//上記を参考にすると以下の挙動も簡単に理解できる。
scala> true || { println("!!"); false }
res4: Boolean = true

scala> false || { println("!!"); false }
!!
res5: Boolean = false


関数引数の遅延評価

まずは正格(遅延しない関数呼び出し側で引数を評価)引数の関数。

def argFunction(value:Int) = None


Scalaの関数引数には式を与えることが可能。なのでこういったことが出来る。

scala> argFunction( {println("呼び出し時に評価");10}  )
呼び出し時に評価
res35: None.type = None

関数呼び出し時に式が評価され、printlnが実行されたのがわかる。式結果の10がInt型の引数に与えられる。

こうすると

def argFunction(value:Int) = println(value)


もちろんこうなる。

argFunction( {println("呼び出し時に評価");10}  )
呼び出し時に評価
10

式を渡せるということがわかったところで、遅延評価を整理していく。




渡された式を関数側で評価する

難しく考える必要はなくて、目的の型を返す関数を引数に渡せばよい。

def lazyFunction(value: () => Int) = None


もちろん関数リテラルを渡すので、関数呼び出し側で式が実行されることはない。

scala> lazyFunction( ()=>{println("呼び出し時に評価");10} )
res38: None.type = None


関数側で式評価するには単純に与えられた関数を実行すればよい。

def lazyFunction(value: () => Int) = value()
scala> lazyFunction(  ()=>{println("関数内で評価");10} )
関数内で評価
res40: Int = 10


遅延表記の簡略化

上記の例では無名関数として遅延評価を行ったが、簡略表記で、あたかも変数のように式を扱うことができる。

関数引数の遅延評価の理屈が分かったら簡略表記も理解できる。引数定義を()なしの無名関数として定義しるだけです。

def lazyFunction(value: => Int) = None


関数内で呼び出す時も()省略で呼び出す。

def lazyFunction(value: => Int) = value //value()の省略


そして呼び出し側も式のみを渡す。

scala> lazyFunction(  {println("関数内で評価");10} )
関数内で評価
res43: Int = 10


遅延評価の確認

では、こうした場合の結果は

def lazyPrintCacheFunction(value: => Int) = {
  println( value + value )
}


こうなるので、関数内でのみ評価されていることがわかる。呼び出し時に評価されているのであれば、関数内で評価の文字は1回のみの出力となる。

scala> lazyPrintCacheFunction( {println("関数内で評価"); 10+5} )
関数内で評価
関数内で評価
30


遅延評価を一度のみ行う lazy

このような記述の場合、当然式評価は2回行われる。

def lazyPrintCacheFunction(value: => Int) = {
  val v1:Int = value //value()
  val v2:Int = value //value()
}

一度のみ評価する場合、まあ、これでもいいんだけど...

def lazyPrintCacheFunction(value: => Int) = {
  val v1:Int = value
  val v2:Int = v1
  println(v1 + v2)
}


このようlazyにして評価遅延させる方法もある。

def lazyPrintCacheFunction(value: => Int) = {
  lazy val v1:Int = value
  println(v1 + v1)
}
ちなみにlazyの実験

lazyの式を遅延評価する関数の引数に渡すと??

def lazyFunction(value: => Int) = None
lazy val v1 = {println("hello"); 1}


評価されない!

scala> lazyFunction(v1)
res58: None.type = None


その後、lazyを評価すると、

scala> v1
hello
res59: Int = 1

scala> v1
res60: Int = 1

どんな時に便利なの?

Haskellでおなじみのリストの遅延評価、無限リストの生成なんかに使える。

Haskell、Scalaって本当に楽しいですね(^_^)