札幌Scala勉強会#12

コップ本第8章の「関数とクロージャー」をやりました。以下メモ。

一人前の存在としての関数

first-class function. wikipediaでは第一級関数。

  • 関数を定義として呼び出せる
  • 関数リテラル
  • 関数リテラルを値として渡せる、戻り値として返せる
  • メタプログラミングとは違う

値としての関数

function values.
関数リテラルを実行時にインスタンス化したもの。
FunctionNトレイトを拡張する何らかのクラスのインスタンス
コップ本にはN(0-22)と書いてあるが、Scaladocを見ても、Function1, Function2しか見当たらない。

部分適用された関数

def sum(a:Int, b:Int, c:Int) = a + b + c 
val a = sum _

sum _ という部分適用関数式から、渡されていない3個の整数パラメータを受け取る関数インスタンスを作成する。

a(1,2,3) #=> 6

これは、a.apply(1,2,3) の短縮形である

someNumbers.foreach( println _ ) 

は、

someNumbers.foreach( println ) 

と書ける。
foreachの引数は関数呼び出しが必要とされる場所だから。

val a = sum _ 

val a = sum

と書けない。これは、右辺が関数呼び出しを保証していないから。

val a:Int => Int = sum

とは書ける。意味違うけど。

クロージャー

自由変数の束縛を「取り込んで」、関数リテラルを「閉じる」

(x:Int) => x + more

moreは自由変数(free variables)。xは束縛変数(bound variables)。
この関数リテラルから実行時に作られるインスタンスをクロージャーと呼ぶ。

閉じた項 : 自由変数のない関数リテラル ← 厳密な意味ではクロージャーではない
開いた項 : 自由変数を含んだ関数リテラル ← 実行時に作られた関数オブジェクト(インスタンス)がクロージャー

プログラムの実行とともに実体が変わっていく変数にアクセスする場合

クロージャーが作成されたときにアクティブだったインスタンスを使う。

scala> def makeInc(more:Int) = (x:Int) => x + more
makeInc: (more: Int)Int => Int

scala> var i = 1
i: Int = 1

scala> val inc1 = makeInc(i)    #=> moreに1が入る。moreの束縛として、1をつかんでクロージャーが作成される
inc1: Int => Int = <function1>

scala> i = 99
i: Int = 99

scala> val inc2 = makeInc(i)    #=> moreに99が入る。
inc2: Int => Int = <function1>

scala> inc1(100)
res0: Int = 101

scala> inc2(100)
res1: Int = 199

連続パラメーター

echo(arr: _*)って分かりづらいね。

末尾再帰

  • 単純再帰しか最適化できない
  • メモ化しているわけではない
  • 同じ関数を呼び出す直接的な再帰のみ