色々な関数のバリエーションがある。
methods, nested within functions, function literals, function values
javaでは出来ない定義方法もある。
関数に部品化する事は分かりやすくするために重要。
8.1 Methods
何らかのobjectのmemberとして定義されているfun
javaでもある普通のmethod。
helper method等はprivateにすることも出来る。
8.2 Local functions
関数の中で関数を定義できる。ローカル変数と同じように。
関数名のスコープは関数内に閉じる。
名前の衝突を気にしなくて良くなる。
private modifierは不要。
上位の関数のスコープ内の変数にもアクセス出来る。
普遍のパラメータを引数で渡す必要がない。
?? 宣言時or実行時どちらの値が使われる??
?? 密結合になって分かり難くならないか??
沢山の小さい関数を作ってそれを積み上げていくやり方を推奨。
その方が分かりやすい。
名前の衝突の問題が出てくるが、local functionsは一つの解決手段。
scalaの全てnest可能と言う概念は単純かつ強力。
8.3 First-class functions
関数リテラルで関数を表したり、
function literalは実行時はfunction valueにインスタンス化される。
classが実行時にはobjectにインスタンス化されるのと
(parameter: annotation, ...) => body
ex. (x: Int) => x + 1
collectionのforeachやfilterメソッドの
bodyは{}で括れば何でも書ける。
8.4 Short forms of function literals
scalaでは冗長情報を省略する記法を色々と用意している。
なるべく簡素に書くようにした方が良い。
関数リテラルの引数の型が推論できる場合は省略出来る。(
関数リテラルの引数の()も省略出来る場合が多い。
x => x + 1
?? とりあえず省略して書いてコンパイラが混乱したら付け加えると良
?? コンパイラが誤解してそのまま通ってしまうと。
8.5 Placeholder syntax
パラメータの記述を省略して、_
_ + 1
annotationもbody側に付ける。
(_: Int) + (_: Int)
一つのパラメータがbodyに一回しか出てこない場合にしか使え
一つのパラメータが繰り返し出てくるときには使えない。
最初に出てきた_が最初のパラメータ、2番目の_
複数のパラメータでも使える。
8.6 Partially applied functions
引数に_を使うことで関数の部分適用が出来る。
def sum(a: Int, b: Int, c: Int) = a + b + c
sum(_: Int, _: Int, 1) // 一つのパラメータを適用したfunction value
sum(_, _, _) // 全部のパラメータを適用していないfunction value
sum _ // 全部のパラメータを適用していないfunction value
sum_はNGでスペースが必要なので注意。
function typeが期待されているときは_を省略することが出来る。
Array(1, 2, 3).foreach(println)
?? sum(_: Int, _: Int, 1)のannotationはどうして省略できない??
?? sum _ と sum(_)は違う??
?? 同じ名前の関数をoverload出来るのはmethodだけ? local functionではNG??
?? 別スコープの関数をshadowするとsignatureが違っ
8.7 Closures
closureはfree variableが生成時に閉じられた(capturing)
x => x + more // more: free variable, x: bound variable
free variableがないfunction literalはclosed term(コードの断片)
x => x + 1
free variableありはopen term
free variableの元変数が変更されたらclosure内の値も
逆にclosure内で値を変更したら元の値も変化する。
?? 値ではなくてリファレンスをcapturingしてる??
var, val, parameterでもlocal variableでも何でもcapturing出来る。
stackに置かれているものでも自動的にheapに置きなおさ
?? パフォーマンス上の問題はでない??
scalaでは関数の部分適用を明示的示すために_
これは古典的な関数型減言語(Haskell,ML)
javaとの親和性を考えて言語仕様のtrade-
root typeなら何でもOKと言う世界だと厳密な型チェックが出来な
間違って関数部分適用の形になってもエラーチェックできないので
8.8 Repeated parameters
最後の引数のannotationに*
def f(args: Int*) = for (arg <- args) println(arg)
関数内ではArray typeとして扱われる。
Arrayを引数として渡したときはannotationで_*
f(Array(1, 2, 3): _*)
?? : Int* ではなんでだめ??
8.9 Tail recursion
再帰呼び出しでも、末尾再帰の最適化でループと同等の性能になる。
ループの方がメモリ消費や処理速度で一般に優れているが、最適化出来れば変わらない。
Tracing tail-recursive functions
末尾再帰と言うのは、再帰呼び出しの後に処理がないこと。
処理の一番最後が再帰呼び出しだと言うこと。
単純に関数の先頭にジャンプする処理に置き換えられる。
exceptionを発生されてtraceを見るとスタックが積まれていないのが見える。
Limits of tail recursion
javaのJVMの実装に依存して最適化出来るパターンが限られている。
複数の関数の相互呼び出しでは最適化されない。
function valueに同一関数を格納して呼び出しても最適化されない。
自分自身を直接最後に呼び出した場合のみ最適化される。
?? 最新コンパイラでも同じ??
8.10 Conclusion
methods, local functions, function literals, function values.
partially applied functions, functions with repeated parameters.
optimized tail calls.
0 件のコメント:
コメントを投稿