Top / Docs / プログラミング言語 / Ruby / 言語ダイジェスト

プログラミング言語/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:
 
 ブロック引数の扱いが変わっています。
 
 - 多重代入のような挙動から、メソッド引数のような扱いに変更されています。
 - 外側の環境に同名のローカル変数があるのに、ブロック引数にもその名前を
   使った場合、外側の環境の変数として扱われていたのに対し、新たに
   ブロックローカルな変数が確保されるようになります。
 
 また、ブロックローカル変数宣言が可能になっています。
 
   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 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
  • until
  • for
  • イテレータ

です。以下これら総称してただ単にループと呼びます。

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
 
 最低限、こんな感じで自分でも実装できます。
📝 このページに誤りを見つけたら…  GitHubで報告(エンジニア向け)  ・  フォームで報告