式評価の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って本当に楽しいですね(^_^)