2014年4月15日火曜日

Programming in Scala, First Edition: Chapter 9

9.1 Reducing code duplication
  関数には呼び出し毎に変わる部分と、変わらない部分がある。
    変わる部分 -> argument
    変わらない部分 -> body
  変わる部分(argument)にfunction valueを渡すことでalgorithmを変えられる。
  higher-order function(高階関数)
    引数に関数をとる関数。
  higher-order functionを使ってcontrol abstractionしてduplicationを減らす。
  function literalを使うことで簡単にfunction valueを渡せる。
  target typingを使うことで型指定を減らせる。
  placeholder, _, を使うことで、parameter記述を更に減らせる。
  clousre, partially applyを使うことで更にparameterを減らせる。
  ** 全てを駆使すると非常に短く書ける。凄い。
  ?? 省略し過ぎて分かり難くならないか少し心配。
  ?? それでも使われ方から関数の引数の型推論をしてくれないのが不便
    ?? HaskellやMLの様にもっと厳密な型システムなら推論できる??
    ?? 全てのtypeの共通の祖先のroot typeがあると、全てその型に集約されて型チェックが働か無くなる?
  動的言語なら実行時にコードをpasteして実行することも出来るかも。(eval)
    shell scriptやtclでは可能。
  ex. fileName.endsWith(query)のfunction valueを考える。
    fileName: String, query: String, endsWith(String, String): Boolean
    全て書くと
      (fileNmae: Strng, query: String) => fileName.endsWith(query)
    target typingで引数の型を省略
      (fileNmae, query) => fileName.endsWith(query)
    clousreを使う
      fileNmae => fileName.endsWith(query)
    placeholderを使う
      _.endsWith(query)

9.2 Simplifying client code
  higher-order functionのAPIを使えばclient codeも簡素化出来る。

9.3 Currying
  ** カリー化とは
    複数の引数をとる関数を、
    引数が「もとの関数の最初の引数」で
    戻り値が「もとの関数の残りの引数を取り結果を返す関数」で
    あるような関数にすること。(Wikipedia)
  scalaでは複数パラーメタリストを持つ関数を書ける。
    def sum(w: Int)(x: Int, y: Int)(z: Int) = w + x + y + z
    sum: (w: Int)(x: Int, y: Int)(z: Int)Int
    ?? この記法をcurryingと呼んでいる??
    ?? 一つのパラメータリストを使う方法とどちらが推奨とかあるのか??
    ?? Haskell等ではこちらの定義の方が一般的
    各パラメータリストに複数パラメータがあっても良い。
    下記と概念的には透過。
    def sum(w: Int) = (x: Int, y: Int) => ((z: Int) => w + x + y + z)
    sum: (w: Int)(Int, Int) => Int => Int
    ?? 型シグネチャは違うが、実装上も違う??
  部分適用も出来る。
    val sum2 = sum(2)_
    sum2: (Int, Int) => Int => Int = <function2>

9.4 Writing new control structures
  first-class functionを持つ言語では、function valueで新しいcontrol structureを作れる。
  control patternの繰り返しがある場合は、新しいcontrol structureを検討する。
  loan pattern
    リソースオープン、実行、クローズのパターン等で使う。
    実行をfunction valueにして、他を固定化することでリソースリークを防ぐ。
  パラメータリスト内の要素が一つの時は()の代わりに{}が使える。
    curryingとBy-name parametersと組み合わせると、built-inの様になる。
  ** 関数を細かく分割すればplaceholderを使えるケースが増えて簡素に出来る。

9.5 By-name parameters
  パラメータ無しの場合、関数リテラルの () => 表記を省略する記法がある。
    By-name parametersと呼ばれる。
    関数のパラメータの場合にのみ。fieldやvariableには使えない。
  関数リテラルを格納したfunction valueを評価するときの()も付けない。
    ()なしで評価されても関数実行される。
  パラメータのannotationで () => の変わりに => を使う。
  ex.
    // (cond: => Boolean)(body: => Unit)
    // By-name parameters, => で表記。()は使わない。
    def myWhile(cond: => Boolean)(body: => Unit): Unit =
      if (cond) { // ()無し評価で関数実行
        body // ()無し評価で関数実行
        myWhile(cond)(body) // パラメータとして渡す時点では評価されない。
      }

    var i = 10
    // curringした関数の第一引数にpartially applyして1引数関数値を生成
    // 1引数なので{}を使って更にfunction valueを渡す
    myWhile (i > 0) { // () => i > 0 のby-name paramaters記法。
      println(i)
      i -= 1
    } // () => {println(i); i -=1} のby-name paramaters記法。
    ** whileと同様の制御構造を自分で定義出来た!!
    ** 引数がある場合はplaceholderを使って簡素化する。

9.6 Conclusion
  control abstractions, higher-order functions, currying, by-name parameters
  上記を使えばbuilt-inの様に見えるcontrol structureを作れる。

0 件のコメント:

コメントを投稿