2014年4月14日月曜日

Programming in Scala, First Edition: Chapter 8

8. Functions and Closures
  色々な関数のバリエーションがある。
    methods, nested within functions, function literals, function values
    javaでは出来ない定義方法もある。
  関数に部品化する事は分かりやすくするために重要。

8.1 Methods
  何らかのobjectのmemberとして定義されているfunction。
  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では冗長情報を省略する記法を色々と用意している。
  なるべく簡素に書くようにした方が良い。
  関数リテラルの引数の型が推論できる場合は省略出来る。(target typing)
  関数リテラルの引数の()も省略出来る場合が多い。
    x => x + 1
  ?? とりあえず省略して書いてコンパイラが混乱したら付け加えると良い??
    ?? コンパイラが誤解してそのまま通ってしまうと。バグになるのでは??

8.5 Placeholder syntax
  パラメータの記述を省略して、_をパラメータのplaceholderとして使える。
    _ + 1
  annotationもbody側に付ける。
    (_: Int) + (_: Int)
  一つのパラメータがbodyに一回しか出てこない場合にしか使えない。
    一つのパラメータが繰り返し出てくるときには使えない。
  最初に出てきた_が最初のパラメータ、2番目の_は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)function value
    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-offでそうなっている。
    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 件のコメント:

コメントを投稿