[[プログラミング言語/Ruby]]

#contents

#br

*Ruby Minumums [#e948fe18]

Rubyは行指向型言語です。つまり、改行が一文の区切りとなります。
* Ruby Language Digest [#xd7d634c]

文末記号としてセミコロン';'も使えるのですが、一行に複数の文を書きたいのでもない限り必要ありませんし、推奨されてもいません。rubyのパーザはとかく巨大で複雑なので、少しでも負担を減らしてあげましょう。
Ruby は行指向型言語です。つまり、改行が一文の区切りとなります。

文末記号としてセミコロン ';' も使えるのですが、一行に複数の文を書きたいのでも
ない限り必要ありませんし、推奨されてもいません。

  class NoVipError < StandardError ; end

↑セミコロンで文を区切る頻出例。中身のない例外クラスの定義。

なお、Cと同様に、バックスラッシュを打つことで行を継続させることができますし、メソッド呼び出しの引数も複数行にわたって書くことができます。
なお、C言語 と同様に、バックスラッシュを打つことで行を継続させることが
できますし、メソッド呼び出しの引数も複数行にわたって書くことができます。

どこでなにが複数行にわたっての記述が可能か、ということはおおむねユーザの直感にそぐうように決められているので、問題はないでしょう(CやPerlプログラマなら、この直感はより鋭くなるでしょう)。
どこでなにが複数行にわたっての記述が可能か、ということはおおむねユーザの直感に
そぐうように決められているので、問題はないでしょう(C や Perl プログラマなら、
この直感はより鋭くなるでしょう)。


**コメント [#k72682c4]

** コメント [#pec5986f]

  #行末までコメントだぜ
シェルスクリプトやPerlと同じです。

シェルスクリプトや Perl や Python と同じです。

  =begin
    RDを書くところ。
  =end

このように、「'=begin'」のみの行から「'=end'」のみの行
までの間もコメントとして処理系には無視される。
クラスやメソッドの説明をRD(Ruby Document)形式で
書くために使われたりする。
このように、「'=begin'」のみの行から「'=end'」のみの行までの間もコメントとして
処理系には無視される。
クラスやメソッドの説明をRD(Ruby Document)形式で書くために使われたりする。

**変数 [#m9b7f45e]
Rubyの変数には型がなく、宣言は初回の代入と同時に
行われます(例外あり)。

Javaな人は「Objectクラスへの参照」型が唯一の型だ、
と考えてもいいかもしれません。Rubyの値はすべてオブジェクト
であり、Objectクラスをスーパークラスに持ちますから。

Rubyの変数には数種類ありますが、その種類はPerlのファニー文字の
ようにプレフィクスに左右されます。
** 変数 [#q44509e9]

:ローカル変数  -- local_variable|
普通に英数字・アンダーバーで名前をつけます。
宣言は代入と同時で、一度も代入していない変数名を参照すると叱られます。
なお、その場合、引数なし・カッコ省略メソッド呼び出しと区別が付かないので、rubyはそのように叱ります。
Ruby の変数には型がありません。
「すべてがオブジェクト」である Ruby では、変数はそのオブジェクトへの参照です。

:グローバル変数 -- $global_variable|
Rubyの変数には数種類あり、識別子の先頭で書き分けます。


: ローカル変数 | local_variable

普通に英数字・アンダーバーで名前を付けます。
宣言は代入と同時、宣言されていないローカル変数の参照は NameError 例外を
起こします。

  Ruby のローカル変数の宣言と評価順序はびみょーにかみ合ってません。
  パースしていく感じで考えて下さい。


: グローバル変数 | $global_variable

危険なアイツ。
「$」を先頭に付けます。一度も代入せずに参照するとnilを返します。そういう意味でも危険です
(「$var」と間違って「$val」とタイプしてもエラーになりません)。
自分で作った変数ならば、「「$」が先頭 = グローバル変数」なのですが、組み込み変数の中には例外があるので注意が必要です。
'$' を先頭につけます。一度も代入せずに参照すると nil を返します。そういう意味
でも危険です。'$var' と間違って '$val' とタイプしてもエラーになりません。

:定数 -- Constant|
Cの感覚な人は、たとえばArrayオブジェクトが入った定数Arrに向かって
 Arr.push("hoge")
とかで内容を変えられることに困惑するかもしれません。
この場合、定数として変更不可能なのは「参照先」です。「参照先のオブジェクトの内容に関する変更の禁止」は、また別の方法があります。
大文字から名前が始まるとそれは定数とされ、constやfinalのような特別な修飾はありません。宣言(つまり代入)されていない定数を参照するとエラーになります。
自分で作った変数だと「'$' が先頭 -> グローバル変数」なのですが、組み込み変数の中には例外(スレッドローカルだったり)があるので注意が必要です。

:インスタンス変数 -- @instance_variable|
オブジェクトの状態。
「@」から始まり、宣言なしに参照するとnilを返します。
C++やJavaなひとはメンバ変数のことだと思ってください。
ただし可視性は設定できず、例外なくクラスの外からはアクセスできません。セッタ・ゲッタを定義する必要があります。

:クラス変数 -- @@class_variable|
同じクラスのインスタンスに共通の変数。
「@@」から始まり、宣言なしに参照するとエラーになります。
Javaな人はstaticなメンバと似たようなものだと思ってください。
: 定数 | Constant

**リテラル [#xae3e799]
大文字で始めると定数にされます。 const や final のようなキーワードはありません。
宣言は代入と同時です。宣言されていない定数を参照すると NameError 例外が発生
します。この挙動を返るフックは存在しますが後述します。

Ruby の定数の縛りはちょっとゆるく、定数に再代入(初回の代入(=宣言)以降の代入)
しても現在はエラーではなく警告が出るだけです。

また、Ruby の変数は前述のとおりオブジェクトへの参照なので、定数とするのも
参照です。

  CONST_ARRAY = []
  CONST_ARRAY.push 'hoge'

なので、オブジェクトの操作はし放題です。オブジェクトの変更禁止は別の方法が
あります。


: インスタンス変数 | @instance_variable

Ruby で「オブジェクトの状態」を表現するもの。
'@' を先頭に付けます。一度も代入せずに参照すると nil を返します。

メンバー変数とでも言える機能ですが、オブジェクトの外からのアクセスは
(リフレクション抜きでは)できません。
通常 setter・getter メソッドを定義します。


: クラス変数 | @@class_variable

名前どおりクラスが持つ変数です。
そのクラスと、そのクラスのオブジェクト両方からアクセスできます。さらに、
サブクラスに継承されたりします。若干スコープがややこしいです。

'@@' を先頭に付けます。一度も代入せずに参照すると NameError 例外が発生
します。



** リテラル [#m578e446]

Rubyのリテラル表現は豊富です。以下に代表的なものを挙げます。

***数値リテラル(Numeric) [#s7ad24b5]

|123     |整数|
|4567.89 |実数|
|?a      |aの文字コード|
*** 数値リテラル (Numeric) [#lbc01b3e]

***文字列リテラル(String) [#v8f805f4]
| 123     | 整数          |
| 4567.89 | 実数          |
| ?a      | aの文字コード |

|"double_quote" |式展開などが解釈されます|
|'single_quote' |書いたままになります|
|`back_quote`   |コマンドとして実行し、出力を値とします|

■□式展開
*** 文字列リテラル (String) [#o8ef98d2]

  val = 123
  puts "put #{val} value"
上のコードの出力は
  put 123 value
となります。ダブルクォート文字列中に#{ ... }で囲った
Rubyの式を書くと、それの値を文字列で表現して埋め込みます。
| "double_quote" | 式展開などが解釈されます               |
| 'single_quote' | 書いたままになります                   |
| `back_quote`   | コマンドとして実行し、出力を値とします |

  `back_quote` 構文の挙動は、実は Kernel#` というすごい名前のメソッドが
  実現しています。

***配列リテラル(Array) [#x08adefd]
  式展開とは:
  
    val = 123
    puts "put #{val} value"
  
  このコードを実行すると、出力は
  
    put 123 value
  
  となります。式展開が有効なリテラル中で、#{...} で Ruby の式ならなんでも
  置いてみると、それを評価した値の文字列表現が展開されます。
  
  「式展開が有効なリテラル」と言いましたが、この機能が有効なのはダブルクォート
  文字列に限らず、ヒアドキュメントや正規表現リテラルでも使えたりします。

[12, "abc", 123.45]~
... コンマで区切って要素を指定します。もちろん一つも要素を指定
しない(`[]'; 空の配列)ことも可能です。

***ハッシュリテラル(Hash) [#k6377b8b]
*** 配列リテラル (Array) [#jb3c0ac8]

{ "key" => "value", "KEY" => 123 }~
... コンマで区切ってキーと値のペアを指定します。
  [12, "abc", 123.45, some_method()]

***シンボルリテラル(Symbol) [#y309f60b]
カンマで式を区切って並べて要素を指定します。もちろん一つも要素を指定しないで、
空の配列を作ることも可能です。

:symbol ... symbolというSymbolオブジェクトを生成します。

*** ハッシュリテラル (Hash) [#n1c30305]

**ブロック付きメソッド呼び出し [#t9f4cd37]
メソッドに引数としてオブジェクトを渡すのみならず、処理のカタマリを渡すことができます。
  { "key" => "value", :KEY => 123 }

「キー => 値」という式のペアを、カンマで区切って指定します。もちろん一つも
要素を指定しないで、空のハッシュテーブルを作ることも可能です。

  Ruby 1.9.x feature:
  
    { key1: "value", key2: 123 }
  
  キーが Symbol リテラルの場合、こういった書き方もできます。


*** シンボルリテラル (Symbol) [#g39bb67c]

Symbol オブジェクトは、Ruby の識別子の実体でもあるオブジェクトです。
処理系に作られるものもあれば String#intern メソッドによって作られるものも
あります。

「:ident」のようにリテラルが書けます。「:"*hoge*"」のように、識別子っぽくない
Symbol をリテラルで書くにはクォートします。



** ブロック付きメソッド呼び出し [#x961ec60]

メソッドに引数としてオブジェクトを渡すのみならず、処理のカタマリ=ブロックを
渡すことができます。

  method_name(arg, ...) do |x|
    ...
  end

  method_name(arg, ...){|x|
    ...
  }

のように記述すると、method_name内に処理を引き渡せます。
いわゆるクロージャ、無名関数あたりのことだと思ってもらえると。
のように記述すると、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, ...)
    ...
    yield(x)
    block_returning_value = yield(val)
    ...
  end

引き渡した処理は、yield(x)のように、必要ならば引数を与えてメソッド内で呼び出せます。
イテレータなども、この機能によって実装されています。
この機能によって、制御構文のようなメソッドが Ruby にはいろいろあります。

■[少しわかる人へ]

要はレキシカルクロージャっぽいものを1つだけメソッドに与えてやる
機能が文法として用意されてると思ってもらえれば。

** Rubyの真偽評価 [#sfb10875]

**Rubyの真偽評価 [#jbb0c75c]
以下に述べる条件分岐などで、Rubyの値(=オブシェクト)がどの
ように真偽評価されるかを説明します。
以下に述べる条件分岐などで、Ruby の値(=オブシェクト)がどのように真偽評価される
かを説明します。

結論から言うと、Rubyにおける偽とはfalseとnilであり、
それ以外のあらゆるオブジェクトは真です。
結論から言うと、Ruby における偽とは false と nil であり、それ以外のあらゆる
オブジェクトは真です。

明示的に真を表す代表的な値にtrueがあります。
明示的に真を表す代表的な値に true があります。

これら(true, false, nil)は%%予約語ではなく%%(嘘です。予約語です。が)
それぞれTrueClass, FalseClass, NilClassの唯一のインスタンスです。
これら(true, false, nil)はそれぞれ TrueClass, FalseClass, NilClass の唯一の
インスタンスです。


**制御構造 [#bd2afe6f]
***条件分岐 [#f3190124]
■if文, if修飾子

  if expr then ...
** 制御構造 [#u6da8d06]

  ... if expr

  if expr then
*** 条件分岐 [#k68afffa]

: if 文, if 修飾子 |

  if EXPR then ... end
  
  if EXPR [then]
    ...
  end
  
  ... if EXPR

exprを評価して真ならば、ブロック内を実行する。
最後のブロック形式の書き方では、thenは省略可能。
EXPR を評価して真ならば、... を実行する。
複数行に分けて書くならば(改行で区切りが明確なので) then は省略可能です。

  if expr1 then
    ...  #(a)
  elsif expr2 then
    ...  #(b)
  if EXPR1
    ... # (A)
  elsif EXPR2
    ... # (B)
  else
    ...  #(c)
    ... # (C)
  end

expr1を評価して真ならば(a)部を実行して終わりますが、
そうでない場合、expr2の評価に移ります。
EXPR1 を評価して真ならば (A) を実行して終わりますが、そうでない場合、
EXPR2 を評価して真ならば (B) を実行します。
こうした elsif 部の条件式も全部偽だった場合、(C) の else 部を実行します。

expr2の評価が真ならば(b)を実行し、
それもまた偽ならば(c)を実行します。
elsifはいくつでも使えますし、
もちろんelsifなしでいきなりelseを書いてもOKです。
0 個以上の elsif 部の後に 0 ~ 1 個の else 部が書けます。

■unless文, unless修飾子

ifに似た文法をとりますが、unlessは条件式の評価基準が逆です。
つまり、
: unless 文, unless 修飾子 |

  ... unless expr
if に似た文法をとりますが、unless は条件式の評価基準が逆です。

は、exprの評価が偽のときだけ...を実行しますし、
  ... unless EXPR

  unless expr then
    ... (d)
は、EXPR の評価が偽のときだけ ... を実行しますし、

  unless EXPR
    ... (A)
  else
    ... (e)
    ... (B)
  end

は、exprの評価が真ならば(d)部を実行し、
そうでなければ(e)部を実行します。
ただ、ifにおけるelsifに相当する文はありません。
は、EXPR の評価が真ならば (A) を実行し、そうでなければ (B) を実行します。
ただ、if における elsif に相当するパートはありません。

**繰り返し [#pfda227d]
Rubyにおける繰り返し(ループ)のための文とは、


** 繰り返し [#d785fa55]

Ruby における繰り返し(ループ)のための文とは、

- while
- until
- for
- イテレータ

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

■while文

  while cond do
: while 文 |

  while COND [do]
    ...
  end

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

■until文

  until cond do
: until 文 |

  until COND [do]
    ...
  end

は、if-unlessの関係と同じで、condの評価が偽である限り、
ブロック内を実行します。
while - until は if - unless の関係に対応し、COND の評価が偽である限り、
中の文を実行します。

■for文

  for x in list
: for 文

  for IDENT in LIST_EXPR
    ...
  end

Rubyのfor文はCなどのものと違い、シェルスクリプトやPerlのforeachに近いです。これは、配列のように複数の要素の集合であるlistに対して、その各要素をxに取り出して順次処理していくときに使われます。
Ruby の for 文は C などのものと違い、シェルスクリプトや Perl の foreach に
近いです。
これは、配列のように複数の要素の集合である LIST_EXPR(の評価結果)に対して、
その各要素を IDENT という変数に取り出して順次処理していくときに使われます。

(listの基準 : listのクラスがCollectionだった場合、Collection#eachというメソッドが定義されている必要があります。これはイテレータというやつです。Rubyのforは内部的にこれを利用しています)
in の後にとるオブジェクトには、each というメソッドがイテレータ(後述)として
実装されている必要があります。
for 文は内部でこのメソッドを呼んでいます。

■イテレータ

例:Array#each
: イテレータ | 例: Array#each

  array = [1, 2, 3, 4, 5]
  
  array.each do |x|
    p x #=>1, 2, 3, 4, 5
    p x  #=> 1, 2, 3, 4, 5
  end

ブロック付きメソッド呼び出しを利用して、
わざわざカウンタ用の変数を用意したり、脱出条件を書かなくても全要素
に対して同じ処理を繰り返せます。
ブロック付きメソッド呼び出しで、「要素に対しての繰り返し」を隠蔽して
実装しています。

  loop do
    ...
  end

のように、一見制御構造のようなイテレータから、

  n = 1
  
  n.upto(10) do |x|
  1.upto(10) do |x|
    p x  #=>1, 2, 3, ..., 9, 10
  end

のように、用途が特化した便利なイテレータもあります。

  元々 Ruby のブロック付きメソッド呼び出し機能は、このようなイテレータメソッド
  を実装する狙いで、CLU という言語を参考にしたものです。
  そうした歴史的経緯から、ちょっと前の文書だと
  「ブロック付きメソッド呼び出しのブロック」ぐらいの意味で「イテレータ」という
  言葉が使われていたりします。

■break, next, redo, retry

: break, next, redo, retry |

ループ内でのフロー制御文です。
:break|現在のループから脱出します。
:next|現在のループの、現在の回の残りの文を飛ばして、次の繰り返しに移ります。
:redo|現在のループの、現在の回の繰り返しをまた初めからやり直します。
:retry|現在のループを始めからやり直します。

**例外処理 [#q496224f]
: break | 現在のループから脱出します。
: next  | 現在のループの、現在の回の残りの文を飛ばして、次の繰り返しに移ります。
: redo  | 現在のループの、現在の回の繰り返しをまた初めからやり直します。
: retry | 現在のループを始めからやり直します。



** 例外処理 [#p4e37c64]

  begin
    ...  #(a)
    ...  # (A)
  rescue ExClass => ex
    ...  #(b)
    ...  # (B)
  else
    ...  #(c)
    ...  # (C)
  ensure
    ...  #(d)
    ...  # (D)
  end

begin ... endブロック内(の、(a)部)で起きた例外を、
rescueで捕捉します。
(A) 内で起きた例外を rescue で捕捉します。

rescueのExClassには捕捉する例外クラスを指定し、
=> ex で、exという変数に捕捉した例外オブジェクトを
代入します。どちらも省略可能で、ExClassを省略した場合は
RuntimeErrorクラスを指定したとみなされます。
捕捉する例外クラス ExClass を指定して、変数 ex で実際に捕捉した
例外オブジェクトを参照します。
「rescue => ex」、「rescue ExClass」のように両者とも省略可能です。ExClass が
省略された場合は StandardError が指定されたものとして処理されます。
rescue 節は、発生した例外のクラスが ExClass、またはそのサブクラスの場合にその
例外を捕捉します。

指定した例外クラスと、その全てのサブクラスのオブジェクトを
捕捉します。
並べた rescue 節がひとつも実行されなかった場合、(C) の else 部が実行されます。

elseは1つ以上のrescueと組で、例外が発生しなかった場合、
(c)部を実行します。
そして最後に、例外が起きようが起きまいが、どの rescue 節あるいは else 節が実行
されようがされまいが、必ず締めに実行されるのが (D) の ensure 節です。

ensure以下の(d)部は、例外が捕捉されようがされまいが
実行する文を記述します。
1 個以上の rescue 節のあとに 0 ~ 1 個の else 節が書けます。
そのあとに 0 ~ 1 個の ensure 節が書けます。

もちろん、rescue、else、ensureは省略可能です。
…が、rescueの一つも書かずにbegin ... endでくくっても
しょうがないんですが。

**クラス定義 [#pb39a7b7]
せっかくの純オブジェクト指向言語ですから、クラスの作り方をば。

  class ClassName < SuperClassName
** クラス定義 [#z33d56dc]

せっかくのオブジェクト指向言語ですから、クラスの作り方をば。

  class ClassName < SuperClass
    ...
  end

「< SuperClassName」は名の通り継承するクラスを指定します。省略すると自動的にObjectクラスを継承します。
「< SuperClass」は名の通り継承するクラスを指定します。省略すると自動的に
Object クラスを継承します。評価すればスーパークラスを返す式ならば置けます。

ClassNameは大文字で始める必要があります。定数と同じです(というか、まんま定数なのですが)。
ClassNameは大文字で始める必要があります。定数と同じです(というか、まんま定数
なのですが)。

定義したクラスのインスタンスはobj = ClassName.new()のカタチで作れます。カタチを見て判るとおり、newは予約語ではなくメソッド名で、普通に変数名とかにも使えます。
定義したクラスのインスタンスは obj = ClassName.new() のカタチで作れます。
カタチを見て判るとおり、new は予約語ではなくメソッド名で、普通に変数名とかにも
使えます。

生成したクラスを初期化したい場合、C++やJavaでいうコンストラクタのようなものを定義します。class ... end内で定義されるinitialize()というメソッドがそれです。newは自分の引数をinitializeに丸投げします。
生成したクラスを初期化したい場合、C++ や Java でいうコンストラクタのような
ものを定義します。class ... end 内で定義される initialize() というメソッドが
それです。new は自分の引数を initialize に丸投げします。


クラスと似たようなものにモジュールがあります。

  module ModuleName
    ...
  end

モジュールはクラスと違って継承できず、インスタンスも持てません。

しかし、include ModuleNameによってそのモジュールで定義した機能をクラス定義中に取り込むことができます(Mix-In)。
しかし、include によってそのモジュールで定義した機能を
クラス定義中に取り込むことができます(Mix-In)。

■アクセス制御


*** メソッド可視性の制御 [#t530a764]

  class ClassName
  public
    ... #(A)
  private
    ... #(B)
  protected
    ... #(C)
    public
    ...  # (A)
    private
    ...  # (B)
    protected
    ...  # (C)
  end

(A)で定義したメソッドはクラス定義の外から(つまりそのクラスのインスタンスをレシーバにして)呼び出せます。
C++ や Java と語彙が一緒なので誤用されていることがたまにあります。

(B)で定義されたメソッドはクラス定義内からしか呼び出せません。
(A) で定義されたメソッドは public で、どこからでも呼べます。

(C)で定義されたメソッドですが、C++やJavaのprotectedとは意味合いが違い、自分のクラスとそのサブクラスからだけ呼べるメソッドになります。
(B) で定義されたメソッドは private で、そのオブジェクト自身のコンテキストで
しか呼べません。

■attr系メソッド -- アクセサ定義
(C) で定義されたメソッドは protected で、private のような制御が成されますが、
自分のクラスがそのオブシェクトのクラスと同じ、あるいはサブクラスであるような
オブシェクトのコンテキストでは public な制御がされます。

Rubyオブジェクトのインスタンス変数は外からはアクセスできません。がセッタとゲッタを定義することでその問題を回避します。
Ruby のメソッド呼び出し制御の着眼点は、「レシーバ.メソッド」という形で
呼び出すかどうか、に集約されます。

その場合、set_member(val)、get_member()のような名前で定義するのはRubyでは一般的ではありません。member=(val)、member()のように定義して、あたかもメンバやらプロパティへの代入や参照のように見せかけることができます。インスタンス変数名とメソッド名が衝突しないからです。
  class Hoge
    def initialize
      priv       # これはOK
      self.priv  # NoMethodError
    end
    
    private
    
    def priv
      ...
    end
  end

ただ、外からアクセスする必要があるインスタンス変数全てに、いちいち
  def member()
    @member
同じコンテキストですらこうです。

それから、いくら private にしようがリフレクションでどうにでもなるゆるゆるな制御
なので、あてにし過ぎるのは止してください。



*** attr系メソッド -- アクセサ定義 [#pa36a72a]

「変数」の節で

  メンバー変数とでも言える機能ですが、オブジェクトの外からのアクセスは
  (リフレクション抜きでは)できません。
  通常 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
  
  def member=(val)
    @member = val

これで、インスタンス変数をメソッドで属性のように扱えます。
定義する際においては他のメソッドとなんら変わりなく、代入値のチェックなども
自分で勝手に書けます。

ただ、そういった独自処理もしないのにいちいち上記のような setter・getter を手で
書くのは煩雑です。なのでこれに等しい処理は

  class C
    attr_accessor :member
  end
などと記述しているのは無駄すぎます。そこで、上記のような定義を行うことを請け負ってくれるメソッドがあります。
- attr_reader ..... 前者(ゲッタ)を定義
- attr_writer ..... 後者(セッタ)を定義
- attr_accessor ... 両方を定義

で可能です。

: 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