0. はじめに
C/C++, C#, Java, Perl, PHP, Python など他言語の経験者が Ruby に触れる際の導入として、
オブジェクト周りを中心に Ruby の基本をざっくり理解するための資料です。
irb (Interactive Ruby) という対話的実行環境コマンドを使って実際に遊びながら読み進めれば、
1時間くらいで Ruby の特徴や感触が掴めると思います。
なお、Ruby のインストール方法や文法・ライブラリの網羅などはここでは扱いません。
それらを知りたい場合は以下の公式ドキュメントが参考になります。
https://www.ruby-lang.org/ja/documentation/
1. Ruby ではすべてがオブジェクト
Ruby にはいわゆる基本データ型(プリミティブ型)がありません。
整数・浮動小数点数・文字列・配列・連想配列・true
・false
・nil
など、
すべてがオブジェクト(特定クラスのインスタンス)になります。
# 整数
irb> 1.class
=> Fixnum
# 浮動小数点数
irb> 1.0.class
=> Float
# 文字列
irb> "abc".class
=> String
# 配列
irb> [1, 2, 3].class
=> Array
# 連想配列
irb> {"key" => "value"}.class
=> Hash
# true
irb> true.class
=> TrueClass
# false
irb> false.class
=> FalseClass
# nil(他言語でいう null)
irb> nil.class
=> NilClass
オブジェクトである以上、メソッドが使えます。
- 例えば
1.next
など、クラス毎に様々なメソッドが定義されています - 加算
1 + 1
の+
ですらメソッドです(これは1.+(1)
のシンタックスシュガーです) - 配列参照
ary[0]
の[]
もメソッドです(arr.[](0)
のシンタックスシュガーです) - 配列代入
ary[0] = 1
の[]=
もメソッドです(arr.[]=(0, 1)
のシンタックスシュガーです)
2. Ruby ではすべてのクラスが再定義可能
Ruby では組み込みクラスを含むすべてのクラスが再定義可能です。
既存クラスを再定義(再オープン)することで、メソッドの追加・上書きなどが容易にできます。
例えば以下のようなことが可能です。
irb> class Fixnum
irb> def megabytes
irb> self * 1024 * 1024
irb> end
irb> end
=> :megabytes
irb> 1.megabytes
=> 1048576
さらに、以下のようなことも可能です。(良い子はマネしちゃダメ!)
irb> class Fixnum
irb> def +(other)
irb> self - other
irb> end
irb> end
=> :+
irb> 1 + 2
=> -1
3. Ruby の基本オブジェクトで遊ぶ
3.1 整数(Fixnum
オブジェクト)
irb> 1 + 1
=> 2
irb> 2 * 8
=> 16
irb> 2 ** 16
=> 65536
irb> 2.next
=> 3
irb> 2.next.next.next # このように「メソッドチェイン」も可能
=> 5
3.2 浮動小数点数(Float
オブジェクト)
irb> 1.5 + 1
=> 2.5
irb> 2.5 * 8
=> 20.0
irb> 1.6.floor
=> 1
irb> 1.6.round
=> 2
3.3 文字列(String
オブジェクト)
irb> "abc" + "d"
=> "abcd"
irb> "bye" * 2
=> "byebye"
irb> "abc".upcase
=> "ABC"
irb> "abc".next
=> "abd"
irb> "abc".next.upcase.reverse
=> "DBA"
3.4 配列(Array
オブジェクト)
irb> ary = [1, 2, 3]
=> [1, 2, 3]
irb> ary[0]
=> 1
irb> ary[1]
=> 2
irb> ary[2]
=> 3
irb> ary[-1]
=> 3 # -nは後ろからn番目の要素を参照する
irb> ary[4]
=> nil # 範囲外の要素を参照してもエラーにならない
irb> ary[9] = 10
=> 10
irb> ary
=> [1, 2, 3, nil, nil, nil, nil, nil, nil, 10] # 歯抜けの要素は nil でパディングされる
3.5 連想配列(Hash
オブジェクト)
irb> hash = {"key" => "value"}
=> {"key" => "value"}
irb> hash["key"]
=> "value"
irb> hash["hoge"]
=> nil # 未定義のキーで参照してもエラーにならない
irb> hash["foo"] = "bar"
=> "bar"
irb> hash
=> {"key" => "value", "foo" => "bar"}
上記の例では Hash
のキーに文字列(String
オブジェクト)を使っていますが、
シンボル(Symbol
オブジェクト)を使うことも多いです。
詳細は 5.3 文字列とシンボルは違うモノ(Hash のキー利用時の注意点) を参照してください。
3.6 オブジェクトの変換メソッド(to_*)
Ruby の各クラスには to_i
, to_f
, to_s
, to_a
, to_h
といった型変換的なメソッドが定義されています。
これらの定義有無はクラスや Ruby のバージョンによってバラツキがあります。
# -> Fixnum
irb> 1.to_i # 自分自身を返す
=> 1
# -> Float
irb> 1.to_f
=> 1.0
# -> String
irb> 1.to_s
=> "1"
# -> Fixnum
irb> 3.99.to_i # 小数点以下切り捨て
=> 3
# -> Float
irb> 1.0.to_f # 自分自身を返す
=> 1.0
# -> String
irb> 1.0.to_s
=> "1.0"
irb> (1.0 / 0.0).to_s
=> "Infinity"
# -> Fixnum
irb> "1".to_i
=> 1
irb> "3.99".to_i # 小数点以下切り捨て
=> 3
irb> "foo".to_i # 非数字文字列ではエラーにならず0を返す
=> 0
irb> "".to_i # 空文字でも0を返す
=> 0
irb> "12foo".to_i # 頭に数字が含まれる場合は解釈できるところまでを変換対象とする
=> 12
# -> Float
irb> "1".to_f
=> 1.0
irb> "3.99".to_f
=> 3.99
irb> "foo".to_f # 非数字文字列ではエラーにならず0.0を返す
=> 0.0
irb> "".to_f # 空文字でも0.0を返す
=> 0.0
irb> "12foo".to_f # 数字が含まれる場合は解釈できるところまでを変換対象とする
=> 12.0
# -> String
irb> "foo".to_s
=> "foo" # 自分自身を返す
# -> Array
irb> [1, 2, 3].to_a
=> [1, 2, 3] # 自分自身を返す
# -> Hash
irb> [[1, 2], ["foo", "bar"]].to_h # Ruby 2.1 以降で利用可能
=> {1 => 2, "foo" => "bar"}
# -> Array
irb> {1 => 2, "foo" => "bar"}.to_a
=> [[1, 2], ["foo", "bar"]]
# -> Hash
irb> {1 => 2, "foo" => "bar"}.to_h
=> {1 => 2, "foo" => "bar"} # 自分自身を返す
nil
(NilClass
オブジェクト)の変換メソッドは0や空を表すオブジェクトを返します。
# -> Fixnum
irb> nil.to_i
=> 0
# -> Float
irb> nil.to_f
=> 0.0
# -> String
irb> nil.to_s
=> ""
# -> Array
irb> nil.to_a
=> []
# -> Hash
irb> nil.to_h
=> {}
4. Ruby でのオブジェクトの扱われ方(メモリ内部イメージで理解する)
Ruby でのオブジェクト生成・参照について、メモリ内部のイメージ図と共に具体例で説明します。
例1:
str = "foo"
=> "foo"
ary = [str, str]
=> ["foo", "foo"] # ① これは予想通りの結果
str = "bar"
=> "bar"
ary
=> ["foo", "foo"] # ② なぜ ["bar", "bar"] にならない!?
例2:
irb> str = "foo"
=> "foo"
irb> ary = [str, str]
=> ["foo", "foo"] # ① これは予想通りの結果
irb> str << "bar"
=> "foobar"
irb> ary
=> ["foobar", "foobar"] # ② これも予想通りの結果
irb> str = str + "baz"
=> "foobarbaz"
irb> ary
=> ["foobar", "foobar"] # ③ なぜ ["foobarbaz", "foobarbaz"] にならない!?
4.1 破壊的メソッドと非破壊的メソッド
自分自身を変更するメソッドが「破壊的メソッド」です。
自分自身を変更しないメソッドが「非破壊的メソッド」です。
先の例では String
クラスの <<
が破壊的メソッド、+
が非破壊的メソッドです。
Ruby では最後に !
の有無だけが異なる同名メソッドをしばしば見かけます。
その場合は基本的に !
有りが破壊的メソッド、!
無しが非破壊的メソッドになります。
# upcase!(破壊的メソッド)
irb> str1 = "abc"
=> "abc"
irb> str2 = str1.upcase!
=> "ABC"
irb> str1
=> "ABC" # str1 が変更されている(さらに言うと、str1, str2 は同一オブジェクトを参照している)
# upcase(非破壊的メソッド)
irb> str1 = "abc"
=> "abc"
irb> str2 = str1.upcase
=> "ABC"
irb> str1
=> "abc" # str1 は変更されていない(さらに言うと、str1, str2 は異なるオブジェクトを参照している)
ただし、以下のように !
が無い破壊的メソッドも存在します。
-
String
クラスの<<
-
Array
クラスのshift
-
Hash
クラスのupdate
4.2 mutable なオブジェクトと immutable なオブジェクト
破壊的メソッドがあるのが「mutable(変更可能)なオブジェクト」です。
破壊的メソッドがないのが「immutable(変更不可能)なオブジェクト」です。
これまでに出てきたオブジェクトの mutable / immutable は以下の通りです。
- mutable: 文字列・配列・連想配列
- immutable: 整数・浮動小数点数・
true
・false
・nil
5. その他、知らないとハマるかもしれないあれこれ
5.1 Ruby での真偽判定
式の評価結果が nil
または false
の場合のみ偽で、それ以外のオブジェクトではすべて真となります。
0
でも真というのはハマりやすいポイントなので注意です。
irb> def check(value)
irb> value ? true : false
irb> end
=> :check
# 偽判定になるパターン
irb> check(nil)
=> false
irb> check(false)
=> false
# 真判定になるパターン
irb> check(0)
=> true
irb> check(0.0)
=> true
irb> check("0")
=> true
irb> check("nil")
=> true
irb> check("false")
=> true
irb> check("")
=> true
irb> check([])
=> true
irb> check({})
=> true
なお、Ruby において ==
, <
, <=
, =>
, >
などは各クラスで定義されているメソッドです。
そのため、比較内容はクラスによって異なります。(クラスによってはメソッド自体が定義されていない場合もあります)
5.2 Ruby には ++
, --
が無い
Ruby では整数(Fixnum
オブジェクト)が「immutable なオブジェクト」として設計されています。
そのため、++
, --
のように自分自身を破壊的に増減させる術はありません。
その代わりに i += 1
, i -= 1
を使います。
5.3 文字列とシンボルは違うモノ(Hash
のキー利用時の注意点)
例えば文字列 "foo"
とシンボル :foo
は属しているクラスが異なるので、全く違うオブジェクトです。
-
"foo"
は mutable なString
オブジェクト -
:foo
は immutable なSymbol
オブジェクト
これを混同すると、Hash
での値取得でハマることになります。
irb> hash = {'key' => 'value'} # 文字列でキーを定義
=> {'key' => 'value'}
irb> hash['key']
=> 'value' # 文字列では値を取得できる
irb> hash[:key]
=> nil # シンボルでは値を取得できない(:key というキーは未定義だから)
なお、文字列とシンボルはそれぞれ to_sym
, to_s
メソッドで相互に変換可能です。
irb> "foo".to_sym
=> :foo
irb> :foo.to_s
=> "foo"
これを利用して、Rails(Webアプリケーションフレームワーク)の環境下では HashWithIndifferentAccess という Hash
の拡張クラスにより文字列とシンボルのどちらを使っても値を取得できるようになっています。
このため、Rails から Ruby を始めた場合には「文字列とシンボルは同じもの」と誤解することがあるかもしれません。
5.4 Hash
のキーに mutable な String
オブジェクトを使っても大丈夫なの!?
大丈夫です。
Hash
のキーに String
オブジェクトを使用した場合、それをコピーした新たなオブジェクトへの参照を保持します。
このため、キーに使用した String
オブジェクトを破壊的に変更しても、キーはその影響を受けません。
irb> str = "foo"
=> "foo"
irb> hash = {str => str}
=> {"foo" => "foo"} # この時点でキーは str と異なるオブジェクトを参照している
irb> str << "bar" # ここで str が参照するオブジェクトを破壊的に変更してみる
=> "foobar"
irb> hash
=> {"foo" => "foobar"} # 値は破壊的変更の影響を受けているが、キーは影響を受けていない
irb> hash["foo"]
=> "foobar" # 従って、"foobar" でなく "foo" で値を取得できる
ただし、Hash
のキーに Array
オブジェクトなどを使用した場合はこのようなコピー処置は行われません。
(そんなことをする人はあまりいないと思いますが・・・)
6. おわりに
以上、オブジェクト周りを中心に Ruby の基本について解説しました。
個人的に、Ruby は書くことが楽しいプログラミング言語であると感じます。
その理由の一つに「Ruby ではすべてがオブジェクト」という特徴が挙げられるのではないかと思っています。
本記事が少しでも Ruby 導入への手助けになれれば幸いです。
また、不明点やツッコミがあれば随時コメントいただけると嬉しいです。