Essential Scala 第2章 式、型、値 / Essential Scala Chapter 2 Expressions, Types, and Values

Essential Scala 第2章 式、型、値 / Essential Scala Chapter 2 Expressions, Types, and Values

4abe386a96c2939093c526ae5b59f77f?s=128

Takuya Tsuchida

June 27, 2018
Tweet

Transcript

  1. Essential Scala 第2章 式、型、値 2018.6.27 Takuya Tsuchida (@takuya0301)

  2. 第2章 式、型、値 2.1 最初のプログラム 2.2 オブジェクトとの相互作用 2.3 リテラルオブジェクト 2.4 オブジェクトリテラル

    2.5 メソッドの書き方 2.6 複合式 2.7 まとめ 2
  3. 第2章 式、型、値 本章では Scala プログラムの基本的な構成要素である式、型、値について見ていきま す。それらのコンセプトを理解することが Scala プログラムがどう動くかというメンタルモ デルを形成するために重要です。 3

  4. 2.1 最初のプログラム Scala コンソールでは評価結果の型と値が表示されます。 "Hello world!" // res: String =

    Hello world! Scala のコードは左から右へ評価されます。 "Hello world!".toUpperCase // res: String = HELLO WORLD! 4
  5. 5

  6. 2.1.1 コンパイル時と実行時 コンパイル時には文法と型が検証されます。 toUpperCase."Hello world!" // error: identifier expected but

    string literal found. // toUpperCase."Hello world!" // 2.toUpperCase // error: value toUpperCase is not a member of Int // 2.toUpperCase // 6
  7. 2.1.1 コンパイル時と実行時 実行時にエラーが発生することもあります。 2/0 // java.lang.ArithmeticException: / by zero 7

  8. 8

  9. 2.1.2 式、型、値 式はプログラムテキストでコンパイル時に存在しています。プログラムテキストの構成要 素として定義や文も存在します。 型は、値が一貫性をもって解釈されるようにプログラム記述を制約するもので、コンパイ ル時に存在しています。型消去によって実行時には存在しません。 値はメモリに保存された情報で実行時に存在しています。Scala の値はすべてオブジェ クトです。 9

  10. 10

  11. 2.1.3 キーポイント:最初のプログラム Scala のメンタルモデルにおいて式、型、値が基本的な構成要素です。 式はプログラムの部品で値に評価されます。式は型を持ちます。 型はプログラム上の制限を表現し、コンパイル時にチェックされます。 値はメモリ上に存在し、実行しているプログラムが操作する対象です。Scala の値はす べてオブジェクトです。 11

  12. 2.2 オブジェクトとの相互作用 12 前節では Scala プログラムの基本的な構成要素である式、型、値を見ました。また、す べての値はオブジェクトであることも学びました。本章ではオブジェクトとオブジェクトとの 相互作用について学びます。

  13. 2.2.1 オブジェクト オブジェクトはデータとそのデータに対する操作の組み合わせのことです。 操作をメソッドと呼びます。 データはフィールドに保存されます。 13

  14. 2.2.2 メソッド呼び出し メソッドを呼び出すことでオブジェクトを作用させます。* "Hello world!".toUpperCase // res: String = HELLO

    WORLD! パラメーター(引数)を受け取れるメソッドもあります。 "abcdef".take(3) // res: String = abc 14 * オブジェクトを作用させる別の方法としてパターンマッチングがあります。
  15. 15

  16. ノート:メソッド呼び出し文法 anExpression.methodName(param1, ...) anExpression.methodName • anExpression はオブジェクトに評価される式 • methodName はメソッドの名前

    • param1, ... はメソッドの引数として評価される1つ以上の式 16
  17. 17 2.2.3 演算子 Scala のすべての値はオブジェクトなので、プリミティブ型*に対してもメソッドが呼び出せ ます。 123.toShort // this is

    how we define a `Short` in Scala // res: Short = 123 Scala では演算子もメソッドです。Scala のメソッドはシンボルによる名前を使用できま す。 43 - 3 + 2 // res: Int = 42 43.-(3).+(2) // res: Int = 42 * Java のプリミティブ型にマッピングされる Int や Boolean などです。
  18. ノート:中置演算子記法 a.b(c) と記述されるすべての Scala の式は a b c と記述できる。 a

    b c d e は a.b(c).d(e) と等価である。 18
  19. 19 2.2.3 演算子 すべての1引数メソッドは中置記法にできます。 "the quick brown fox" split "

    " // res: Array[String] = Array(the, quick, brown, fox) 前置記法、後置記法、右結合、代入演算子という記法もありますが中置記法に比べると 一般的ではありません。 Scala における演算子の優先順位*はメソッドの名前から推論されます。 * https://www.scala-lang.org/files/archive/spec/2.12/06-expressions.html#infix-operations
  20. 20

  21. 2.2.4 キーポイント:オブジェクトとの相互作用 Scala の値はすべてオブジェクトです。メソッドを呼び出すことでオブジェクトを作用させ ます。 Scala は数少ない演算子しか持たず、ほとんどすべてがメソッド呼び出しです。中置演 算子記法のような文法変換によってシンプルかつ読みやすいコードを維持しています。 21

  22. 2.3 リテラルオブジェクト 22 すでにいくつかの Scala の基本型を説明しました。本節では Scala の全リテラル式を網 羅することでその知識を完全なものにします。リテラル式はそれ自身が表す固定値を表 現します。

    42 // res: Int = 42 リテラルと評価された値を混同しないでください。リテラル式はプログラムを実行する前 のプログラムテキストにおける表現で、値はプログラムを実行した後のメモリ上における 表現です。
  23. 2.3.1 数値 数値は Java に存在する同じ型を共有していて、32ビット整数の Int、64ビット浮動小数 点数の Double、32ビット浮動小数点数の Float、64ビット浮動小数点数の Double

    が あります。 42 // res: Int = 42 42.0 // res: Double = 42.0 42.0f // res: Float = 42.0 42L // res: Long = 42 23
  24. 24

  25. 2.3.2 真偽値 真偽値は Java と同じで true か false です。 true

    // res: Boolean = true false // res: Boolean = false 25
  26. 26

  27. 2.3.3 文字 文字は16ビットの Unicode 値で、シングルクォートで囲まれた単一の文字で表現しま す。 'a' // res: Char

    = a 27
  28. 28

  29. 2.3.4 文字列 文字列は Java と同じで、ダブルクォートで囲まれた文字列で表現します。 "this is a string" //

    res: String = this is a string "the\nusual\tescape characters apply" // res: String = // the // usual escape characters apply 29
  30. 30

  31. 2.3.5 Null Null は Java と同じですが、Java ほど頻繁には利用されません。Scala の null は

    Null 型を持ちます。 31
  32. 32

  33. 2.3.6 Unit Unit は Java の void と同じで、() で表現します。Unit は、標準出力に使用される

    println など、興味深い値に評価されない式の結果で使用されます。 多くの Scala の文法的構成要素は式で型と値を持ちます。有用な値を生成しない式の ためのプレースホルダーとして Unit は必要になります。 33
  34. 34

  35. 2.3.7 キーポイント:リテラルオブジェクト 本節では基本データ型に評価されるリテラル式を見ました。基本型のほとんどは Unit を 除いて Java と同じです。 すべてのリテラル式は型を持ち値に評価されます。 35

  36. 2.4 オブジェクトリテラル 36 本節ではオブジェクトリテラルを使用して独自設計のオブジェクト生成します。オブジェク トリテラルを記述するときは、式ではなく宣言を使用します。 空のオブジェクトを宣言します。これは値に評価されませんが、Test という名前に「空の オブジェクト」という値が束縛されます。 object Test

    {} Test // res: Test.type = Test$@2c8c42c Test を評価すると Test.type という型に評価されます。Test.type は、このオブジェクト のためにつくられた新しい型でシングルトン型と呼ばれます。
  37. 37

  38. ノート:オブジェクト宣言記法 object name { declarationOrExpression ... } • name はオブジェクトの名前

    • declarationOrExpression は宣言や式(省略可能) 38
  39. 2.4.1 メソッド オブジェクトはメソッドを通して作用されます。 object Test2 { def name: String =

    "Probably the best object ever" } Test2.name // res: String = Probably the best object ever 39
  40. 2.4.1 メソッド これはより複雑なメソッドの例です。 object Test3 { def hello(name: String) =

    "Hello " + name } Test3.hello("Noel") // res: String = Hello Noel 40
  41. 41

  42. ノート:メソッド宣言記法 def name(parameter: type, ...): resultType = bodyExpression or def

    name: resultType = bodyExpression • name はメソッドの名前 • parameter は1つ以上の引数の名前(省略可能) • type はメソッドの引数の型 • resultType はメソッドの結果の型(省略可能) • bodyExpression はメソッドが呼び出されたときに評価される式 42
  43. ノート:return は暗黙的 メソッドの戻り値は本体を評価することによって決定される。Java でやるように return を 書く必要はない。 43 * メソッドの戻り値はメソッド本体の式の評価結果になります。Java

    のように return を記述する必要はありません。
  44. 2.4.2 フィールド オブジェクトはフィールドにほかのオブジェクトを持てます。 object Test4 { val name = "Noel"

    def hello(other: String): String = name + " says hi to " + other } Test4.hello("Dave") // res: String = Noel says hi to Dave 44
  45. 2.4.2 フィールド val で不変フィールドを定義できます。不変フィールドは名前に束縛された値を変更する ことができません。var で可変フィールドを定義できます。可変フィールドは名前に束縛さ れた値を変更できます。 常に var より

    val を選びましょう。Scala プログラマーは不変フィールドを使用することを 好みます。本書の大部分では var を避けているので、Scala プログラミングをするときは 真似してみてください。 45
  46. 46

  47. ノート:フィールド宣言記法 val name: type = valueExpression or var name: type

    = valueExpression • name はフィールドの名前 • type はフィールドの型(省略可能) • valueExpression はオブジェクトに評価されて名前に束縛される 47
  48. 2.4.3 メソッド対フィールド フィールドは値に名前を与え、メソッドは値を生成する計算に名前を与えます。 object Test7 { val simpleField = {

    println("Evaluating simpleField") 42 } def noParameterMethod = { println("Evaluating noParameterMethod") 42 } } 48
  49. 2.4.3 メソッド対フィールド オブジェクトは遅延ローディングされるため、参照されるまで評価されません。 Test7 // Evaluating simpleField // res: Test7.type

    = Test7$@b22e8c9 フィールドの式はただ1回だけ実行され、その結果がフィールドに保存されます。 Test7.simpleField // res: Int = 42 Test7.simpleField // res: Int = 42 49
  50. 2.4.3 メソッド対フィールド メソッドの式はメソッドが呼び出されるたびに実行されます。 Test7.noParameterMethod // Evaluating noParameterMethod // res: Int

    = 42 Test7.noParameterMethod // Evaluating noParameterMethod // res: Int = 42 50
  51. 51

  52. 2.4.4 キーポイント:オブジェクトリテラル 本節では式を参照するメソッドとフィールドが与えられた独自のオブジェクトを生成しまし た。 オブジェクト・メソッド・フィールドを宣言する文法を見てきました。 それらすべては名前に値を束縛します。宣言は式と異なり、値として評価されず、型も持 ちません。 また、メソッドとフィールドには、フィールドはオブジェクトに保存された値を参照します が、メソッドは値を生成する計算を参照するという違いがあります。 52

  53. 2.5 メソッドの書き方 53 本書では文法を越えて Scala プログラムを構築する体系的な方法を与えることが主目 的のひとつです。 本節ではメソッドを構築するための体系的な方法を見ていきます。Scala についての経 験がある場合、方法のいくつかのステップを省略できるかもしれませんが、本書を読ん

    でいる間はこの方法を踏襲することを強く推奨します。 このアドバイスを具体的にするために前節の課題を例題として使用します。 例題:calc オブジェクトを square メソッドを持たせて定義してください。square メソッド は Double を引数とし、その入力を2乗します。
  54. 2.5.1 入力と出力を確認する メソッドにおける引数の型と結果の型を確認します。 例題の説明によると引数の型は Double です。結果の型も Double であると推測できま す。 54

  55. 2.5.2 テストケースを準備する 型はすべてのストーリーを語りません。Double から Double への関数はたくさんありま すが、2乗を実装しているものは一握りです。なので、メソッドの期待される振る舞いを描 き出すためにテストケースを準備します。 assert(square(2.0) ==

    4.0) assert(square(3.0) == 9.0) assert(square(-2.0) == 4.0) 55 * 本書では外部依存を避けるためにテストライブラリを使用しません。
  56. 56

  57. 57 2.5.3 宣言を書く 型とテストケースがあるのでメソッド宣言を書くことができます。未実装であるメソッドの 本体には Scala の機能である ??? を使用できます。 def

    square(in: Double): Double = ??? この宣言はこれまでのステップで集めた情報によって機械的にもたらされるべきです。
  58. 58

  59. 59 2.5.4 コードを実行する コンパイルとテストの失敗をチェックするためにコードを実行します。

  60. 60

  61. 2.5.5 本体を書く メソッドの本体を書く技法は2つあります。 1つ目は結果の型について考える技法です。例題の結果の型は Double です。どうやっ たら Double を生成できるでしょうか?リテラルを書くこともできますが、当然ながらこの ケースでは正しくありません。

    2つ目は引数の型について考える技法です。例題の引数の型は Double です。Double を生成する必要性も確認済みです。入力から Double を生成するメソッドにどんなもの があるでしょうか?そのようなメソッドはたくさんあるので、ドメイン知識を活用し、呼ぶべ き正しい * メソッドを選択します。 def square(in: Double): Double = in * in 61
  62. 2.5.6 コードを再実行する コードを再実行してテストがすべてパスすることをチェックします。 62

  63. 63

  64. ノート:メソッドの書き方についての手順 1. メソッドにおける入力と出力の型を確認する。 2. 与えられた入力例から期待されるメソッドの出力についてテストケースを記述する。 それらのケースを書くために assert 関数を利用する。 3. 下記のように本体に

    ??? を使用してメソッド宣言を記述する。 def name(parameter: type, ...): resultType = ??? 4. テストケースが失敗することをチェックするためにコードを実行する。 5. メソッドの本体を記述する。そのために下記のような2つの技法がある。 • 結果の型について、それのインスタンスをどう作成できるか検討する。 • 入力の型について、それをどう結果の型に変換できるか検討する。 6. コードを再実行し、テストケースをパスすることをチェックする。 64
  65. 2.6 複合式 65 本節では、より複雑なプログラムを書くために必要な2つの特別な式、条件式とブロック について見ていきます。

  66. 2.6.1 条件式 条件式はいくつかの条件にもとづいて式を選択することを可能にします。 if (1 < 2) "Yes" else "No"

    // res: String = Yes 選択されなかった式は評価されません。 if (1 < 2) println("Yes") else println("No") // Yes 66
  67. 67

  68. ノート:条件式は式である Scala の if 文は Java と同じ文法を持つ。重要な違いは Scala の条件式は式であるの で型を持ち値を返す。

    68
  69. ノート:条件式記法 if(condition) trueExpression else falseExpression • condition は Boolean 型の式

    • trueExpression は条件が true に評価されるときに評価される式 • falseExpression は条件が false に評価されるときに評価される式 69
  70. 2.6.2 ブロック ブロックは一連の計算をまとめる式です。それらはセミコロンもしくは改行で区切られた 式を中括弧で囲むように書きます。 { 1; 2; 3 } //

    some warnings... // res41: Int = 3 70
  71. 2.6.2 ブロック ブロックは式を順に評価し、最後の式における値を返す式です。 ブロックを利用するひとつの理由として、最後の値を計算する前に副作用を発生させた いということがあります。 { println("This is a side-effect")

    println("This is a side-effect as well") 3 } // This is a side-effect // This is a side-effect as well // res: Int = 3 71
  72. 2.6.2 ブロック ブロックを利用するもうひとつの理由として、中間の結果に名前をつけたいということが あります。 def name: String = { val

    title = "Professor" val name = "Funkenstein" title + " " + name } name // res: String = Professor Funkenstein 72
  73. 73

  74. ノート:ブロック式記法 { declarationOrExpression ... expression } • declarationOrExpression は宣言か式(省略可能) •

    expression はブロック式の型と値を決定する式 74
  75. 2.6.3 キーポイント:複合式 条件式は条件にもとづいて式を選択することを可能にします。条件式は式なので型を持 ちオブジェクトに評価されます。 if (condition) trueExpression else falseExpression ブロックは式と宣言を順に並べることを可能にし、副作用の発生と中間の結果への命名

    で利用されます。最後の式がブロックの型と値になります。 { declarationOrExpression ... expression } 75
  76. 2.7 まとめ 76 下記のような Scala の基本について簡潔に紹介しました。 • 値に評価される式 • 値に名前を与える宣言

    様々なオブジェクトのためにリテラルを書く方法、メソッド呼び出しや複合式で既存のも のから新しいオブジェクトを作る方法を見てきました。 また、メソッドとフィールドで構成される独自のオブジェクトを宣言することもできるように なりました。 次章ではオブジェクトを作成するためのテンプレートとしてクラスの宣言を見ていきます。 クラスはコードの再利用と類似するオブジェクトの共通型としての統一を可能にします。