Skip to content

Latest commit

 

History

History
490 lines (418 loc) · 12 KB

expression.md

File metadata and controls

490 lines (418 loc) · 12 KB

リテラル、変数、式

数値 {#numbers}

-
  - 種類
  - Ruby
  - Scala
  - 備考
-
  - 整数
  - '123'
  - |
    123
    2147483647L
  - 整数は32bit(Int),64bit(Long)の2種類に分かれる。RubyのBignumと違い大きさに制限がある。
-
  - 実数
  - '123.45'
  - |
    123.45
    123.
    123.45d
    123.45f
  -  実数はDoubleとFloat
-
  - 指数表記
  - '1.2e-3'
  - '1.2e-3'
  - ''
-
  - 16進整数
  - '0xffff'
  - '0xffff'
  - ''
-
  - 8進数
  - '0377'
  - ''
  - ゼロから始める8進数表記はScala 2.11から非推奨
-
  - 2進整数
  - '0b1011'
  - ''
  - 2進数のリテラルなし
-
  - 桁区切り文字
  - '1_000_000_000'
  - ''
  - 桁区切り文字はなし

Longよりも大きな整数

BigIntクラスを使う

BigInt(Long.MaxValue)  // => scala.math.BigInt = 9223372036854775807
BigInt(Long.MaxValue) + 1  // => scala.math.BigInt = 9223372036854775808
BigInt(9223372036854775808)  // => error: integer number too large
BigInt("9223372036854775808")  // => scala.math.BigInt = 9223372036854775808

Longの最大値よりも大きな数を数値リテラルとして直接記述することはできない。 BigIntへのコンストラクタに渡す必要があるときは文字列として記述する。

実数はBigDecimalクラスを使う。

文字列 {#strings}

-
  - 種類
  - Ruby
  - Scala
  - 備考
-
  - ダブルクォート
  - '"this is a string expression\n"'
  - '"this is a string expression\n"'
  - ''
-
  - シングルクォート
  - '''this is a string expression'''
  - ''
  - シングルクォートはChar型やシンボルを表すため文字列には使えない
-
  - シンボル
  - ':aaa'
  - '''aaa'
  - |
    シングルクォートを単語の先頭だけにつけるとシンボルを表す式になる。
    'a' のように1文字の両端を囲むとChar型のオブジェクトになる。
-
  - ヒアドキュメント
  - |-
    <<EOF
    foo
    bar
    baz
    EOF
  - |-
    """foo
    bar
    baz
    """
  - ヒアドキュメントはないので、改行を含む文字列はRaw Stringで代用する。バックスラッシュによるエスケープが効かない。
-
  - 式埋め込み
  - '"foo #{1 + 2} baz"'
  - 's"foo ${1 + 2} baz"'
  - ダブルクォートの前に小文字のs (interpolator) を置く
-
  - フォーマット
  - |
    sprintf("foo %d baz", 123)
    "foo %d baz" % [123]
  - '"foo %d baz".format(123)'
  - ''
-
  - 正規表現
  - /^[abc]+/
  - '"^[abc]+".r'
  - 正規表現専用のリテラル表記はないが、文字列オブジェクトの .r というメソッドを呼び出す事でRegexクラスのオブジェクトを生成する

変数 {#variables}

-
  - 種類
  - Ruby
  - Scala
  - 備考
-
  - 変数
  - a = 123
  - 'var a: Int = 123'
  - ''
-
  - 定数
  - A = 123
  - 'val a: Int = 123'
  - 変数をvalで宣言すると再代入がコンパイルエラーになる(Rubyの定数より厳格)

変数と型

Rubyとの大きな違いとして、Scalaはコンパイル時に変数やメソッド、関数の型が決定される。 下記のようなケースはコンパイルエラーとなる。 (メソッドと関数は異なるものだが、以下では明確な区別が必要なとき以外は広い意味で「関数」として総称する)

  • 変数に対して決められた型以外の値を代入する
  • 関数に対して決められた型以外の引数を渡す
  • 関数が決められた型以外の値を返す

型注釈は型推論によって省略できる場合もあるが、複雑なケースでは推論できないこともある。

変数の型注釈

val 変数名: 型名 =var 変数名: 型名 =

型推論の例

val name = "たろう"

変数nameの型はStringであることが推論されている。

メソッド、関数 {#functions}

メソッドはクラスやオブジェクトに属する。関数はクラスやオブジェクトとは独立している。

-
  - 種類
  - Ruby
  - Scala
  - 備考
-
  - メソッド
  - |
    def foo(i, j)
      i + j
    end
  - |
    def foo(i: Int, j:Int): Int = {
      i + j
    }
  - '引数と返り値の型注釈、関数本体のブラケットなどを省略せず書いた場合'
-
  - 無名関数
  - |
    f = ->(a,b){ a + b }
    f.call(1, 2) # => 3
  - |
    val f = (a: Int, b: Int) => { a + b }: Int
    f.apply(1, 2) // => 3
  - applyを省略して f(1,2) と書くことができる

メソッドの型注釈

def メソッド名(引数名: 型名, 引数名: 型名): 返り値の型名 = {
  本体
}

値を返す必要がない場合、Unitという型を記述する。

メソッドと関数の型推論

Intクラスの .+() メソッドはIntの値を返すことから、foo()の返り値の型が Int であることが推論できる。 そのため、返り値の型は省略できる。

// 返り値の型を省略
def foo(i: Int, j: Int) = {
  i + j
}

Scala Style Guideでは、パブリックなメソッドは明示的に型注釈を書いておくべきとされている。 (可視性などメソッドの詳細についてはオブジェクト志向を参照)

無名関数の型注釈

メソッドと同じく型推論により型注釈を省略できる。

//返り値の型は推論できるが引数の型は省略できない
val f = (a: Int, b: Int) => { a + b }

関数を変数に代入するとき、その変数は「関数」という型になる。 関数の型は、引数の型と返り値の型を => という記号でつないで表す。

// 引数のない関数の場合 (Function0)
() => Int

// 1引数関数の場合 (Function1)
Int => Int

// 2引数関数の場合 (Function2)
(Int, Int) => Int

変数に型を記述すると、関数の側の型注釈を省略することができる。

val f: (Int, Int) => Int = (a,b) => { a + b }

演算子 {#operators}

ScalaもRubyと同じように演算子の一部がメソッドのシンタックスシュガーとして定義されている。

1 + 21.+(2) のシンタックスシュガー

-
  - 種類
  - Ruby
  - Scala
  - 備考
-
  - 代入
  - foo = bar
  - foo = bar
  - 再代入が必要な時はvarで宣言する
-
  - 配列参照
  - array[1]
  - array(1)
  - カッコによる配列参照はメソッド呼び出し .apply(n) のシンタックスシュガー
-
  - 属性参照
  - foo.bar
  - foo.bar
  - ''
-
  - 自己代入
  - foo += 1
  - foo += 1
  - 再代入があるのでvar変数として宣言が必要
-
  - 多重代入
  - |
    foo, bar, baz = 1, 2, 3
    foo, bar, baz = [1, 2, 3]
  - |
    (foo, bar, baz) = (1, 2, 3)  // カッコで囲むとタプル
    Array(foo, bar, baz) = Array(1, 2, 3)
  - 多重代入はパターンマッチのシンタックスシュガー
-
  - 範囲式
  - |
    1 .. 20   # 1 ~ 20
    1 ... 20  # 1 ~ 19
  - |
    1 to 20    // 1 ~ 20
    1 until 20 // 1 ~ 19
  - 整数クラスの .to() や .until() メソッドを使う。引数が1つしかないメソッドはドットやカッコを省略できる。
-
  - アンド条件
  - |
    foo && bar
    foo and bar
  - foo && bar
  - 演算子 and はない
-
  - オア条件
  - |
    foo || bar
    foo or bar
  - foo || bar
  - 演算子 or はない
-
  - 否定
  - |
    !foo
    not foo
  - '!foo'
  - 演算子 not はない
-
  - 3項演算子
  - 'obj == 1 ? foo : bar'
  - 'if (obj == 1) foo else bar'
  - 3項演算子がないのでif式を使う

コメント {#comments}

-
  - 種類
  - Ruby
  - Scala
-
  - 一行コメント
  - '# コメント'
  - // コメント
-
  - ブロックコメント
  - |
    =begin
    コメント
    コメント
    =end
  - |
    /*
    コメント
    コメント
    */

Rubyの =begin ... =end は正確には「埋め込みドキュメント」。

Scalaでは scaladoc というフォーマットにそってコメントを書くことで、ドキュメントの自動生成やIDEでのヒント表示などツールのサポートが得られる。 http://docs.scala-lang.org/style/scaladoc.html

配列、ハッシュ {#arrayhash}

ArrayとHashがあれば大抵間にあうRubyに比べると、Scalaには用途別に様々なコレクションクラスがある。 データ構造によって挙動や動作効率が違ってくるので、違いをおさえて使い分けるべきとされている。 コレクションAPI

Rubyでは配列やハッシュのインスタンスを生成するのに専用のリテラルが用意されているが、Scalaではそういうものは用意されていない。 代わりに、コンパニオンオブジェクトのapplyメソッド(とその省略形)を呼ぶ事でインスタンスを生成することができる。

-
  - 種類
  - Ruby
  - Scala
  - 備考
-
  - 配列
  - '[1, 2, 3]'
  - |
    Array(1, 2, 3)
    List(1, 2, 3)
  - Rubyでの配列にあたるものは、Array, Listなどがある。
-
  - ハッシュ
  - '{foo: "foo", bar: "bar", baz: "baz"}'
  - Map('foo -> "foo", 'bar -> "bar", 'baz -> "baz")
  - RubyでのハッシュにあたるものはMap

Arrayの読み書き

-
  - 種類
  - Ruby
  - Scala
  - 備考
-
  - 値の格納
  - array[0] = 100
  - |
    array(0) = 100
    array.update(0, 100)
  - '`()=`は`update()`メソッドのシンタックスシュガー'
-
  - 値の読み出し
  - array[0]  # => 100
  - |
    array(0)  // => 100
    array.apply(0)
  - '`()`は`apply()`のシンタックスシュガー'

Mapの読み書き

-
  - 種類
  - Ruby
  - Scala
  - 備考
-
  - 値の格納
  - hash["key"] = 100
  - val newMap = map.updated("key", 100)
  - updated()は新しいオブジェクトを作って返す
-
  - 値の読み出し
  - hash["key"]  # => 100
  - |
    map.get("key")  // => Some(100)
    map.get("bad key")  // => None
    map.getOrElse("key", 0)  // => 100
    map.getOrElse("bad key", 0) // => 0
  - ''

ScalaのMap#getはOption型の値を返す。

  • 値があるときは Some(値)
  • 値がないときは None

Map#getOrElseを使うとOptionを介さず値を取り出すことができる。 キーに対応する値がないときは2番目の引数に指定したデフォルト値が返る。

不変コレクションと可変コレクション

単に Mapと記述した場合、不変コレクションの scala.collection.immutable.Map クラスを指す。 可変コレクションの scala.collection.mutable.Map を使用する場合はインポートなどが必要。

MapをはじめとしたScalaのコレクションには immutable(不変)なものと mutable(�可変)なものがある。 両者は効率と安全性のトレードオフで使い分ける。 一般的にはパフォーマンスに問題のない場合は不変コレクションを使うことが推奨される。

型パラメータ

Rubyでは一つの配列の中に数値、文字列、その他オブジェクトなど自由に混在できる。 Scalaでは原則として1種類のオブジェクトだけを格納する。

別の型を格納する型の表記方法は 外側の型[内側の型] となる。[]の中を型パラメータという。

  • 「Intの配列」 Array[Int]
  • 「文字列のリスト」 List[String]

Mapのキーと値のように、内側の型を複数もつ型も定義できる。また、型パラメータはネストできる。

  • 「文字列をキーとして整数を値とするMap」 Map[String, Int]
  • 「シンボルをキーとして整数の配列を値とするMap」 Map[Symbol, Array[Int]]

すべてのクラスの基底クラスであるAnyを指定してArray[Any] という型のインスタンスを用意すればRubyと同様の事はできるが、型安全性の観点から推奨されていない。