初めての Ruby を読んだ

初めての Ruby を読みました。 良書でした。 この本は他の言語を使ったことがある人が Ruby の言語仕様をざっとキャッチアップするのに最適な本な気がします。

自分は本を読んだ後に書かないと知識が残留しない方なので、 自分忘備録として個人的に便利だったことを箇条書きでメモしておきます。

2章 配列とハッシュ

添字代入について

配列 a に対して添字代入は下記のような挙動となります。

irb(main):001:0> a = [1, 2]
=> [1, 2]
irb(main):002:0> a[0] = 3
=> 3
irb(main):003:0> a
=> [3, 2]
irb(main):004:0> a[4]= "4"
=> "4"
irb(main):005:0> a
=> [3, 2, nil, nil, "4"]
irb(main):006:0> a[0, 3] = 'a', 'b', 'c'
=> ["a", "b", "c"]
irb(main):007:0> a
=> ["a", "b", "c", nil, "4"]
irb(main):008:0> a[0..2] = "A"
=> "A"
irb(main):009:0> a
=> ["A", nil, "4"]
a[0, 3] = 'a', 'b', 'c'

^^ index: 0 から 3 要素以外はデータが消えてしまいます。

添字代入の裏側

添字参照式が返す値は、配列が保持しているオブジェクトの参照 実は添字代入式と添字参照式はまったく別物。

  • 添字代入式 ... Array#=メソッドの呼び出し
  • 添字参照式 ... Array#メソッドの呼び出し

よって下記のような現象が発生する。

irb(main):010:0> a = %w[a b]
=> ["a", "b"]
irb(main):011:0> a
=> ["a", "b"]
irb(main):012:0> a[-3]
=> nil
irb(main):013:0> a[-3] = 1
IndexError: index -3 too small for array; minimum: -2
    from (irb):13:in `[]='
  from (irb):13
  from /usr/bin/irb:12:in `<main>'

3章 数値

ビット演算

ビットシフトは下記のように行うことができます。

右にシフト

irb(main):021:0> 1 << 0
=> 1
irb(main):022:0> 1 << 1
=> 2
irb(main):023:0> 1 << 2
=> 4
irb(main):024:0> 1 << 3
=> 8
irb(main):025:0> 1 << 4
=> 16

左にシフト

irb(main):026:0> a = 1 << 4
=> 16
irb(main):028:0> a
=> 16
irb(main):029:0> a >> 1
=> 8

4章 文字列

ヒアドキュメント

EOS が区切り文字で % のあとに送りたい変数をがかけます。

count = database_connector.get_int(<<"EOS" % author.id)
  SELECT COUNT(*)
  FROM book
  WHERE book.author_id = %d
EOS

シンボル

シンボルは唯一性と軽量性が利点。

文字列の場合

irb(main):039:0* str1 = "ruby"
=> "ruby"
irb(main):040:0> str2 = "ruby"
=> "ruby"
irb(main):041:0> str1 == str2
=> true
irb(main):042:0> str1.equal? str2
=> false

シンボルの場合

シンボルは内容が同値であれば必ず同一のオブジェクトとなる。 特定の内容を表すシンボルオブジェクトはプロセス内で唯一となる。

irb(main):043:0> symbol1 = :ruby
=> :ruby
irb(main):044:0> symbol2 = :ruby
=> :ruby
irb(main):045:0> symbol1 == symbol2
=> true
irb(main):046:0> symbol1.equal? symbol2
=> true

オブジェクトとして同一かどうかだけを調べれば良いので、 シンボル同士は文字列同士に比べて高速に同値性を判定できる。

また、内容に対する唯一性を維持しなければならないため、 シンボルオブジェクトは文字列をは異なり変更不能(immutable)。

5章 入出力

特にまとめることはなかったかもです。

6章 変数と式

参照

入力のオブジェクトを破壊せずにオブジェクトを加工したいときには dup を使って複製してから行う。

def describe(name)
  name[2] = ?p
end

^^ これだと破壊的に処理してしまうため、

def describe(name)
  name = name.dup
  name[2] = ?p
end

という感じでオブジェクトを複製します。

多重代入

複数の式を平行して代入することができる。

a, b, c = 1, 2, 3

内部的には

a = 1; b = 2; c = 3

をしているとおもいきやそうではないらしい。

上記とは評価順序が異なり、 代入されようとしている値は、代入が実際に行われるよりも先に完全に計算される。 よって、

a, b = b, a

なども、元々も a 及び b の値がそれぞれ b 及び a に代入され、値の交換が実現できる。

irb(main):062:0> a, b, c = 1, 2, 3
=> [1, 2, 3]
irb(main):065:0> a, b = b, a
=> [2, 1]
irb(main):066:0> a
=> 2
irb(main):067:0> b
=> 1

多値と配列展開

多重代入は多値をいっぺんに代入する記法です。 * とい修飾を使うことで多値を受け取ることができる。

irb(main):068:0> a, *b = 1, 2, 3, 4, 5
=> [1, 2, 3, 4, 5]
irb(main):069:0> b
=> [2, 3, 4, 5]
irb(main):070:0> a
=> 1

また、* が代入の右辺に出現すると、逆に配列を多値に展開してくれる。

irb(main):071:0> array = [1, 2, 3]
=> [1, 2, 3]
irb(main):072:0> a, b, c = *array
=> [1, 2, 3]

irb(main):078:0> a, b, *c, d = *array, 3, 4, 5
=> [1, 2, 3, 3, 4, 5]
irb(main):079:0> a
=> 1
irb(main):080:0> b
=> 2
irb(main):081:0> c
=> [3, 3, 4]
irb(main):082:0> d
=> 5

7章 メソッド

レシーバの省略

擬似変数selfは常に「現在のレシーバ」を指している。 private メソッドは常にレシーバ省略形式で呼び出さなければならない。

Rubyプログラムにおいては、メソッド内外にかぎらずいかなる箇所においてもselfとなるべきオブジェクトが存在する。

irb(main):083:0> self
=> main

ブロック付きメソッドの定義

yield 式は、メソッド内から呼び出し側のブロックをコールバックする構文。

irb(main):092:0> def foo_bar_baz
irb(main):093:1> yield "foo"
irb(main):094:1> yield "bar"
irb(main):095:1> yield "baz"
irb(main):096:1> end
=> nil
irb(main):097:0> foo_bar_baz do |item|
irb(main):098:1* puts item
irb(main):099:1> end
foo
bar
baz
=> nil

8章 オブジェクトとクラス

特異メソッドと特異クラス

Rubyには特定のオブジェクトのためだけのメソッドやクラス、という概念があり、 これらのことを特異メソッド、特異クラスと呼ぶ。

特異クラス

ある特定のインスタンスのためだけにあるクラス。

CENTRAL_REPOSITORY = Object.new

class << CENTRAL_REPOSITORY
  def register(target)
    ...
  end

  def unregister(target)
    ...
  end
end

特異メソッド

概念上はクラスではなく、インスタンスに直接所属しているメソッド。 実装上は、特異メソッドもクラスに属していて、特異クラスのインスタンスメソッドという位置づけ。

irb(main):100:0> message = "Hello"
=> "Hello"
irb(main):101:0> def message.build_greeting(target)
irb(main):102:1> "#{self}, #{target}"
irb(main):103:1> end
=> nil
irb(main):104:0> message.build_greeting("world")
=> "Hello, world"
irb(main):105:0> message2 = "Hello"
=> "Hello"
irb(main):106:0> message2.build_greeting("world")
NoMethodError: undefined method `build_greeting' for "Hello":String
    from (irb):106
    from /usr/bin/irb:12:in `<main>'