Ruby Language Digest#
Ruby は行指向型言語です。つまり、改行が一文の区切りとなります。
文末記号としてセミコロン ‘;’ も使えるのですが、一行に複数の文を書きたいのでも ない限り必要ありませんし、推奨されてもいません。
class NoVipError < StandardError ; end↑セミコロンで文を区切る頻出例。中身のない例外クラスの定義。
なお、C言語 と同様に、バックスラッシュを打つことで行を継続させることが できますし、メソッド呼び出しの引数も複数行にわたって書くことができます。
どこでなにが複数行にわたっての記述が可能か、ということはおおむねユーザの直感に そぐうように決められているので、問題はないでしょう(C や Perl プログラマなら、 この直感はより鋭くなるでしょう)。
コメント#
#行末までコメントだぜシェルスクリプトや Perl や Python と同じです。
=begin
RDを書くところ。
=endこのように、「’=begin’」のみの行から「’=end’」のみの行までの間もコメントとして 処理系には無視される。 クラスやメソッドの説明をRD(Ruby Document)形式で書くために使われたりする。
変数#
Ruby の変数には型がありません。 「すべてがオブジェクト」である Ruby では、変数はそのオブジェクトへの参照です。
Rubyの変数には数種類あり、識別子の先頭で書き分けます。
- ローカル変数
- local_variable
普通に英数字・アンダーバーで名前を付けます。 宣言は代入と同時、宣言されていないローカル変数の参照は NameError 例外を 起こします。
Ruby のローカル変数の宣言と評価順序はびみょーにかみ合ってません。
パースしていく感じで考えて下さい。- グローバル変数
- $global_variable
危険なアイツ。 ‘$’ を先頭につけます。一度も代入せずに参照すると nil を返します。そういう意味 でも危険です。’$var’ と間違って ‘$val’ とタイプしてもエラーになりません。
自分で作った変数だと「’$’ が先頭 -> グローバル変数」なのですが、組み込み変数の中には例外(スレッドローカルだったり)があるので注意が必要です。
- 定数
- Constant
大文字で始めると定数にされます。 const や final のようなキーワードはありません。 宣言は代入と同時です。宣言されていない定数を参照すると NameError 例外が発生 します。この挙動を返るフックは存在しますが後述します。
Ruby の定数の縛りはちょっとゆるく、定数に再代入(初回の代入(=宣言)以降の代入) しても現在はエラーではなく警告が出るだけです。
また、Ruby の変数は前述のとおりオブジェクトへの参照なので、定数とするのも 参照です。
CONST_ARRAY = []
CONST_ARRAY.push 'hoge'なので、オブジェクトの操作はし放題です。オブジェクトの変更禁止は別の方法が あります。
- インスタンス変数
- @instance_variable
Ruby で「オブジェクトの状態」を表現するもの。 ‘@’ を先頭に付けます。一度も代入せずに参照すると nil を返します。
メンバー変数とでも言える機能ですが、オブジェクトの外からのアクセスは (リフレクション抜きでは)できません。 通常 setter・getter メソッドを定義します。
- クラス変数
- @@class_variable
名前どおりクラスが持つ変数です。 そのクラスと、そのクラスのオブジェクト両方からアクセスできます。さらに、 サブクラスに継承されたりします。若干スコープがややこしいです。
‘@@’ を先頭に付けます。一度も代入せずに参照すると NameError 例外が発生 します。
リテラル#
Rubyのリテラル表現は豊富です。以下に代表的なものを挙げます。
数値リテラル (Numeric)#
| 123 | 整数 |
|---|---|
| 4567.89 | 実数 |
| ?a | aの文字コード |
文字列リテラル (String)#
| “double_quote” | 式展開などが解釈されます |
|---|---|
| ‘single_quote’ | 書いたままになります |
back_quote | コマンドとして実行し、出力を値とします |
`back_quote` 構文の挙動は、実は Kernel#` というすごい名前のメソッドが
実現しています。 式展開とは:
val = 123
puts "put #{val} value"
このコードを実行すると、出力は
put 123 value
となります。式展開が有効なリテラル中で、#{...} で Ruby の式ならなんでも
置いてみると、それを評価した値の文字列表現が展開されます。
「式展開が有効なリテラル」と言いましたが、この機能が有効なのはダブルクォート
文字列に限らず、ヒアドキュメントや正規表現リテラルでも使えたりします。配列リテラル (Array)#
[12, "abc", 123.45, some_method()]カンマで式を区切って並べて要素を指定します。もちろん一つも要素を指定しないで、 空の配列を作ることも可能です。
ハッシュリテラル (Hash)#
{ "key" => "value", :KEY => 123 }「キー => 値」という式のペアを、カンマで区切って指定します。もちろん一つも 要素を指定しないで、空のハッシュテーブルを作ることも可能です。
Ruby 1.9.x feature:
{ key1: "value", key2: 123 }
キーが Symbol リテラルの場合、こういった書き方もできます。シンボルリテラル (Symbol)#
Symbol オブジェクトは、Ruby の識別子の実体でもあるオブジェクトです。 処理系に作られるものもあれば String#intern メソッドによって作られるものも あります。
「:ident」のようにリテラルが書けます。「:"hoge"」のように、識別子っぽくない Symbol をリテラルで書くにはクォートします。
ブロック付きメソッド呼び出し#
メソッドに引数としてオブジェクトを渡すのみならず、処理のカタマリ=ブロックを 渡すことができます。
method_name(arg, ...) do |x|
...
end method_name(arg, ...){|x|
...
}のように記述すると、method_name 内でこのブロックを呼び出すことが 出来ます。|…| で囲われた部分は、そのブロックが受け取る引数のためのローカル変数です。 メソッドの引数のようなものですが、メソッドと違ってブロックからは外側の ローカル変数を見ることが出来ます。
ざっくりと言ってしまうと、高階関数に渡すための無名関数のリテラル、と言えます。
例えば Lisp だと
(mapcar #'(lambda (x) (* x 10)) '(1 2 3))
; => (10 20 30)
のようなことが、Ruby では
[1, 2, 3].map{|x| x * 10 }
# => [10, 20, 30]
と書けます。 Ruby 1.9.x feature:
ブロック引数の扱いが変わっています。
- 多重代入のような挙動から、メソッド引数のような扱いに変更されています。
- 外側の環境に同名のローカル変数があるのに、ブロック引数にもその名前を
使った場合、外側の環境の変数として扱われていたのに対し、新たに
ブロックローカルな変数が確保されるようになります。
また、ブロックローカル変数宣言が可能になっています。
method_name(arg, ...){|x; a, b|
...
}
これで a や b も、外側のスコープに気兼ねせずに使える名前に
なります。ブロックを受け取った側では、yield を使ってブロックを関数呼び出しのように 呼び出せます。
def method_name(arg, ...)
...
block_returning_value = yield(val)
...
endこの機能によって、制御構文のようなメソッドが Ruby にはいろいろあります。
Rubyの真偽評価#
以下に述べる条件分岐などで、Ruby の値(=オブシェクト)がどのように真偽評価される かを説明します。
結論から言うと、Ruby における偽とは false と nil であり、それ以外のあらゆる オブジェクトは真です。
明示的に真を表す代表的な値に true があります。
これら(true, false, nil)はそれぞれ TrueClass, FalseClass, NilClass の唯一の インスタンスです。
制御構造#
条件分岐#
- if 文, if 修飾子
if EXPR then ... end
if EXPR [then]
...
end
... if EXPREXPR を評価して真ならば、… を実行する。 複数行に分けて書くならば(改行で区切りが明確なので) then は省略可能です。
if EXPR1
... # (A)
elsif EXPR2
... # (B)
else
... # (C)
endEXPR1 を評価して真ならば (A) を実行して終わりますが、そうでない場合、 EXPR2 を評価して真ならば (B) を実行します。 こうした elsif 部の条件式も全部偽だった場合、(C) の else 部を実行します。
0 個以上の elsif 部の後に 0 ~ 1 個の else 部が書けます。
- unless 文, unless 修飾子
if に似た文法をとりますが、unless は条件式の評価基準が逆です。
... unless EXPRは、EXPR の評価が偽のときだけ … を実行しますし、
unless EXPR
... (A)
else
... (B)
endは、EXPR の評価が真ならば (A) を実行し、そうでなければ (B) を実行します。 ただ、if における elsif に相当するパートはありません。
繰り返し#
Ruby における繰り返し(ループ)のための文とは、
- while
- until
- for
- イテレータ
です。以下これら総称してただ単にループと呼びます。
- while 文
while COND [do]
...
endCOND の評価が真である限り、中の文を実行します。do は省略可能です。
- until 文
until COND [do]
...
end- while - until は if - unless の関係に対応し、COND の評価が偽である限り、
- 中の文を実行します。
for 文
for IDENT in LIST_EXPR
...
endRuby の for 文は C などのものと違い、シェルスクリプトや Perl の foreach に 近いです。 これは、配列のように複数の要素の集合である LIST_EXPR(の評価結果)に対して、 その各要素を IDENT という変数に取り出して順次処理していくときに使われます。
in の後にとるオブジェクトには、each というメソッドがイテレータ(後述)として 実装されている必要があります。 for 文は内部でこのメソッドを呼んでいます。
- イテレータ
- 例: Array#each
array = [1, 2, 3, 4, 5]
array.each do |x|
p x #=> 1, 2, 3, 4, 5
endブロック付きメソッド呼び出しで、「要素に対しての繰り返し」を隠蔽して 実装しています。
loop do
...
endのように、一見制御構造のようなイテレータから、
1.upto(10) do |x|
p x #=>1, 2, 3, ..., 9, 10
endのように、用途が特化した便利なイテレータもあります。
元々 Ruby のブロック付きメソッド呼び出し機能は、このようなイテレータメソッド
を実装する狙いで、CLU という言語を参考にしたものです。
そうした歴史的経緯から、ちょっと前の文書だと
「ブロック付きメソッド呼び出しのブロック」ぐらいの意味で「イテレータ」という
言葉が使われていたりします。- break, next, redo, retry
ループ内でのフロー制御文です。
- break
- 現在のループから脱出します。
- next
- 現在のループの、現在の回の残りの文を飛ばして、次の繰り返しに移ります。
- redo
- 現在のループの、現在の回の繰り返しをまた初めからやり直します。
- retry
- 現在のループを始めからやり直します。
例外処理#
begin
... # (A)
rescue ExClass => ex
... # (B)
else
... # (C)
ensure
... # (D)
end(A) 内で起きた例外を rescue で捕捉します。
捕捉する例外クラス ExClass を指定して、変数 ex で実際に捕捉した 例外オブジェクトを参照します。 「rescue => ex」、「rescue ExClass」のように両者とも省略可能です。ExClass が 省略された場合は StandardError が指定されたものとして処理されます。 rescue 節は、発生した例外のクラスが ExClass、またはそのサブクラスの場合にその 例外を捕捉します。
並べた rescue 節がひとつも実行されなかった場合、(C) の else 部が実行されます。
そして最後に、例外が起きようが起きまいが、どの rescue 節あるいは else 節が実行 されようがされまいが、必ず締めに実行されるのが (D) の ensure 節です。
1 個以上の rescue 節のあとに 0 ~ 1 個の else 節が書けます。 そのあとに 0 ~ 1 個の ensure 節が書けます。
クラス定義#
せっかくのオブジェクト指向言語ですから、クラスの作り方をば。
class ClassName < SuperClass
...
end「< SuperClass」は名の通り継承するクラスを指定します。省略すると自動的に Object クラスを継承します。評価すればスーパークラスを返す式ならば置けます。
ClassNameは大文字で始める必要があります。定数と同じです(というか、まんま定数 なのですが)。
定義したクラスのインスタンスは obj = ClassName.new() のカタチで作れます。 カタチを見て判るとおり、new は予約語ではなくメソッド名で、普通に変数名とかにも 使えます。
生成したクラスを初期化したい場合、C++ や Java でいうコンストラクタのような ものを定義します。class … end 内で定義される initialize() というメソッドが それです。new は自分の引数を initialize に丸投げします。
クラスと似たようなものにモジュールがあります。
module ModuleName
...
endモジュールはクラスと違って継承できず、インスタンスも持てません。
しかし、include によってそのモジュールで定義した機能を クラス定義中に取り込むことができます(Mix-In)。
メソッド可視性の制御#
class ClassName
public
... # (A)
private
... # (B)
protected
... # (C)
endC++ や Java と語彙が一緒なので誤用されていることがたまにあります。
(A) で定義されたメソッドは public で、どこからでも呼べます。
(B) で定義されたメソッドは private で、そのオブジェクト自身のコンテキストで しか呼べません。
(C) で定義されたメソッドは protected で、private のような制御が成されますが、 自分のクラスがそのオブシェクトのクラスと同じ、あるいはサブクラスであるような オブシェクトのコンテキストでは public な制御がされます。
Ruby のメソッド呼び出し制御の着眼点は、「レシーバ.メソッド」という形で 呼び出すかどうか、に集約されます。
class Hoge
def initialize
priv # これはOK
self.priv # NoMethodError
end
private
def priv
...
end
end同じコンテキストですらこうです。
それから、いくら private にしようがリフレクションでどうにでもなるゆるゆるな制御 なので、あてにし過ぎるのは止してください。
attr系メソッド – アクセサ定義#
「変数」の節で
メンバー変数とでも言える機能ですが、オブジェクトの外からのアクセスは
(リフレクション抜きでは)できません。
通常 setter・getter メソッドを定義します。と述べました。
さて、その setter と getter ですが、set_member(val), get_member() のような ネーミングは Ruby では一般的ではありません。
obj.member = 123
obj.memberあたかも属性のように、こう書きたいものです。そして Ruby では実際にこのような書き方が出来ます。Ruby パーサは、前者を member=() という名前のメソッド呼び出しと 解釈してくれるからです。
class C
def member
@member
end
def member=(val)
@member = val
end
endこれで、インスタンス変数をメソッドで属性のように扱えます。 定義する際においては他のメソッドとなんら変わりなく、代入値のチェックなども 自分で勝手に書けます。
ただ、そういった独自処理もしないのにいちいち上記のような setter・getter を手で 書くのは煩雑です。なのでこれに等しい処理は
class C
attr_accessor :member
endで可能です。
- attr_reader
- getter を定義
- attr_writer
- setter を定義
- attr_accessor
- 両方を定義
特別な構文のように見えますが、実はリフレクションを駆使して定型処理を便利に
まとめた 1 メソッドに過ぎません。
def my_attr_reader(name)
define_method(name){ instance_variable_get("@#{name}") }
end
def my_attr_writer(name)
define_method("#{name}="){|val| instance_variable_set("@#{name}", val) }
end
最低限、こんな感じで自分でも実装できます。