2014年4月14日月曜日

Programming in Scala, First Edition: Chapter 7

7. Built-in Control Structures
  scalaの組み込み制御構文は少ししかない。
    if, while, for, try, match と function call
  便利な構文を組み込む代わりに、ライブラリとして実装している。
  制御構文も値を持つ。
    関数型言語的なアプローチを取っている。
    コードを簡素化できる。
      中間変数を減らせる。
    バグを減らせる。
      あるブランチの戻り値設定忘れを減らせる。

7.1 If expressions
  ifが値を持つことで中間変数を減らして読みやすくなる。
  また、varを減らしてより関数型的に書けるようになる。
    等式推論(equational reasoning)のサポート
      名前とその表現が置き換え可能であると言う事。
      ?? 遅延評価や並列実行をしやすくする??
  refactoringしやすくなる。

7.2 While loops
  whileとdo-whileのloop構造がある。
  意味のある値を返さない。式(expression)ではない。
    実行後の値は():Unitになる。
  Unit型の値は()のみ。
    値があるのでjavaのvoidと違って比較なども可能。
      (while (false) {}) は常にtrueになる。
        常にtrueになるとのwarningは出るが。。。
      ** 逆に例外が投げられないので意図していない動作を見逃す可能性あり。
      ?? この辺りのエラーが出にくい仕様が欠点かもしれない??
      ?? 静的だが動的型付けの思想が入っている? なるべく実行しようとする?
  代入メソッド=の戻り値も():Unitになる!!
    代入された値が評価結果になるjavaやcと異なる。注意が必要
    a = 1, a += 1 の結果は()
    (a = 1) == 1 はfalse。しかもwarningが出る。
  whileの仕様は関数型的ではないのであまり推奨はしない。
    命令型的な書き方に慣れている人のために残してある。
    recursiveで代用できる。

7.3 For expressions
  Iteration through collections
    for (i <- 1 to 4) ...
    "i <- 1 to 4" syntax は generator
      1 to 4 は Range type
      1 until 4 だと 1, 2, 3
      iはvalでイテレーションの度に生成、初期化される。
    ループカウンタを使うやり方より優れている。
      初期値、終了値、インクリメントの方法など気にしなくて良い。

  Filtering
    for (i <- 1 to 4 if i % 2 == 0; if i != 3) body
    if clauseをつけることでbodyの実行対象をfilter出来る。
    ifは;で区切れば複数書ける。and条件。
      改行を入れる場合でも;必要な(事がある)ので注意が必要。
      ()ではなくて{}を使えば;の代わりに改行でOK。
    ifのconditionは()不要。

  Nested iteration
    for (
      i <- 1 to 4 if i % 2 == 0;
      j <- 10 to 14 if j != 13
    ) println(i + " " + j)
    generator, filterの組を複数書くことで多重ループを作れる。
    上が外側のループで下が内側のループ。
    区切りの;は改行しても省略出来ない。{}を使えば改行のみでOK。

  Mid-stream variable bindings
    for {
      i <- 1 to 10 if i % 2 == 0
      k = i * 2 if i % 3 == 0
      j <- 11 to 20 if j != 13
    } println(i + " " + k)
    forの中で変数束縛も出来る。valと同等で不変の変数。
    ?? ifの前にも後ろにも置ける??
    ?? 内包表現に記載できるものの条件は??

  Producing a new collection
    for clauses yield body
    bodyの評価結果をcollectionにして返す。forの評価値になる。
    どういうcollectionになるかはgeneratorに依存する。
    bodyの前にyield修飾子が必要。bodyの中ではだめ。
      Range typeだとVector()になる。
      StringだとVector, ArrayはArray, ListはList...

  ?? どういうcollectionのかよくわからない。
  ?? for (4 <- 1 to 4) でもエラーにならない。一回しか回らない。
  ?? for (), for {}の使い分けが分からない。
  ?? 関数定義しないでインラインで書けば型推論が使える。
  ?? パラメータの型指定必須なのが使いにくい??
  ?? 型パラメータを使えばいい??

7.4 Exception handling with try expressions
  scalaのexceptionは他の言語と似ている。
  exceptionが発生すると処理を中断して上位に戻る。
  exceptionをhandleしなかったらその更に上位に伝わる。
  Throwing exceptions
    throw new IllegalArgumentException
    javaと同じ
    scalaではexceptionも戻り値を持っている。
      戻り値の型はNothing
      if式の片側がthrowで戻り値の型がNothingになる場合が多い。
        この場合はif式の方はthrowが無い方の枝の型になる。Anyにはならない。
      ?? Nothingは評価されることがない型??
      ?? Unitは評価されることがありえるので違う??

  Catching exceptions
    try {...} catch { case ex: xxException => }
      他の言語と同じようにtry-cacth式を使う。
      catch節はパターンマッチングのシンタックスを使う。一貫性のため。
    ?? scalaではjavaと違って例外の補足やthow節での宣言を要求しない??
      ?? @throwを使えばjavaと同じように出来る? Section 29.2参照。

  The finally clause
    try {...} finally {...}
      他の言語と同じように例外の有無と関係なく実行される。
    loan patternを使えば同様のことが簡素に書ける。Section 9.4参照。

  Yielding a value
    try-catch-finallyも値を返す。
    finallyでは副作用のある後処理のみをすべきで、main処理の値を書き換えるべきではない。
      try { 1 } finally { 2 } は1を返すが、
      try { return 1 } finally { return 2 } は2を返す。
      ?? どうして??
      これは自然には理解できないので、finallyで値を返すべきではない。

7.5 Match expressions
  値 match { case パターン => 式; case ... }
  javaのswitch-caseに対応するが、さらに拡張されている。
  任意のパターンが使える。整数だけではなくて。
  Match expressions自体も値を返す。
  _ は完全に値が分からない時に使うpalceholder
    case _ ... => ... で全てにmatchするdefaultとして使える。
  breakの記載は不要で、falling throughしない。
  ** パターン書き方は多彩で記述力が高い。Chapter 15参照。

7.6 Living without break and continue
  scalaにはbreakやcontinueがない
  関数リテラルと相性が悪いから。
    この点では関数型に多少傾いている。
  再帰呼び出しや関数リテラルを使った方が簡素に書ける。Chapter 8
  末尾再帰は最適化されるのでスタックの消費は心配要らない。Section 8.9

7.7 Variable scope
  nested scopeで外側と同じ名前を使えるところ以外はjavaと同じ
    shadow
    shadowは混乱の元なのでなるべく使わない方が良い。
  {}が始まるたびに新しいスコープが作られる。
  interpreterではコマンドを打つたびに{}で新しいスコープが作られているのと同様。
    したがって、shadowされてるので同じな名前を別の定義で使える。

7.8 Refactoring imperative-style code
  side effect, while loop, varの使用をやめてfunctional styleにする。
  printの変わりに結果を文字列で保存したほうが単体試験がしやすい。
    print関数を別の関数で置き換えたりするstubの作成も不要。
  helper function
    インラインでも構わないが、読みやすくするために定義している。
    すぐ側の関数を補助するための関数。多くはlocalに定義される。
    ?? これを定義する時にパラメータの型指定が面倒だと思うけど??

7.9 Conclusion
  制御構造は最小限。
  値を持つので関数型スタイルにも使える。
  関数リテラルと言う最も強力な特徴は次章で説明。

0 件のコメント:

コメントを投稿