N Scala応用 関数 覚書

ScalaにはFunction0 ~ 22のトレイトがある。数字は引数の数で、0なら返り値のみ。
関数の作り方をきちんと追うと、まずこれらのトレイトを継承した無名クラスを作り、その中でapplyメソッドを宣言する、という流れ。この辺が関数型言語の特徴なのか?

applyメソッドは特別で、a.apply(x)がa(x)と等しい
ふつう、関数はfunc(x)のように呼び出すが、これを関数がインスタンスとなるScalaで書くとfunc.apply(x)となってしまう。
先程のようにapplyを省略することで普通の書き方になる。

 

Scalaは式ベースの言語であり、中括弧{ } (ブロックという)も実は式。
その返り値はブロック内の最後の式の評価結果なので、{ x + y }とx + yは同じ結果になる。

 

new Function2[String, Int, Int]の大括弧は型パラメータという。たぶん大体ジェネリクス

 

関数とメソッドの違いは、第一級の値であるかどうか。
つまり関数はオブジェクトになる。メソッドはならない
なので、引数としてメソッドを与えたり、メソッドが何かのプロパティを持つことは不可能。

 

高階関数とは、関数を引数にしたり、返り値にする関数のこと。

 

Scalaには末尾再帰最適化という機能がある。これは再帰によるスタックのオーバフローを防止してくれる機能。
末尾再帰による最適化 - Qiita が詳しい。

自分なりに説明してみる。
オーバフローの原因は、再帰する関数の返り値がreturn result + func(a) のようになっていること。
上記のように書くと、func(a)を評価したにresultと足し合わせる。
つまり、呼び出し元のアドレスと result変数をコールスタックに保持しておかなければならない。これでオーバフローする。

一方、return func(result, a) とすると、funcの返り値が呼び出し元の返り値そのものとなる。
この場合、返り値を計算するのに必要な変数は全てfuncに渡される。
つまり呼び出し元の情報を今後一つも必要としないので、もはや呼び出し元のアドレスも変数も必要ない。
よってコンパイラはこれをループに変換でき、オーバフローも回避できる。