プログラミング言語/Ruby

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実数
?aaの文字コード

文字列リテラル (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:
 
 ブロックローカル変数の扱いが変わっています。
 
 - 多重代入のような挙動から、メソッド引数のような扱いに変更されています。
 - 外側の環境に同名のローカル変数があるのに、ブロックローカル変数にもその名前を
   使った場合、外側の環境の変数として扱われていたのに対し、新たに
   ブロックローカル変数が確保されるようになります。

ブロックを受け取った側では、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 EXPR

EXPR を評価して真ならば、... を実行する。 複数行に分けて書くならば(改行で区切りが明確なので) then は省略可能です。

 if EXPR1
   ... # (A)
 elsif EXPR2
   ... # (B)
 else
   ... # (C)
 end

EXPR1 を評価して真ならば (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 文
 while COND [do]
   ...
 end

COND の評価が真である限り、中の文を実行します。do は省略可能です。

until 文
 until COND [do]
   ...
 end

while - until は if - unless の関係に対応し、COND の評価が偽である限り、 中の文を実行します。

: for 文

 for IDENT in LIST_EXPR
   ...
 end

Ruby の 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)
 end

C++ や 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
 
 最低限、こんな感じで自分でも実装できます。

トップ   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS