Ruby – クラスメソッド

概要

クラスメソッドは、インスタンスではなくクラスレベルで定義されるメソッド。

定義と利用の方法

インスタンスメソッドは、メソッド名の前に'self.'を付して定義する。

インスタンスメソッドは、[クラス名].[メソッド名]で呼び出す。

インスタンスからは実行不可

インスタンスメソッドから呼び出したり、インスタンスから直接呼び出すことはできない。

 

Ruby – クラス変数

概要

クラス変数はクラスの全員スタンスで共通して参照・定義される変数。各インスタンスが共通の資産を利用するような場合に使われる。

定義方法

クラス変数の定義方法は以下の通り。

  • クラス定義の直下に書く
  • 変数の頭に'@@'をつける
  • メソッド内で使うときも'@@'をつける

このクラス変数に対して、各インスタンスから変更を加えたときの挙動を以下に示す。

なお、クラス変数はカプセル化されており、クラス・インスタンスのどちらからも直接には参照・定義できない。

インスタンス変数と同様にgetter/setterを定義することで参照可能になる。

 

Ruby – クラス – アクセスメソッド

概要

Rubyのインスタンス変数(プロパティー)は、クラス内に隠蔽(encapsulate)されていて、外部からの参照・変更はできないが、アクセスメソッドを使うことで、外部からの参照・変更が可能となる。

attr_reader

attr_readerメソッドを定義すると、そのインスタンス変数を直接参照できるようになる。ただし値のセットはできない。

attr_writer

attr_writerメソッドを定義すると、そのインスタンス変数に直接値をセットできるようになる。ただし参照はできない。

attr_accessor

attr_accessorメソッドを定義すると、そのインスタンス変数の読み書きが直接できるようになる。

無定義での利用

各アクセスメソッドは、initialize()などでインスタンス変数に対してオブジェクトの実態を定義しなくても機能する。ただし無定義のままではnilのままなので、利用時にエラーとなることがある。

initialize()などで明示的に定義するのが望ましい。

複数定義

各アクセスメソッドで','で区切って複数のシンボルを並べることができる。

私見

最初にインスタンス変数(プロパティー)のカプセル化を知ったとき、Pythonに比べてオーソドックスなオブジェクト指向だと感じたが、アクセスメソッドの扱いは腑に落ちない。この方法はC++で言えば単にプロパティーをpublic指定したようなもので、トラブル軽減や体系的な処理において難点がある。

たとえばinitialize()で初期化をせずにアクセスメソッドで定義した場合にオブジェクトの実態が定義されていないので、それを利用する場合にエラーとなる可能性が高くなる。

また。商品の価格をプロパティーとして、それが負の値となった場合に例外を投げるような処理が必ず必要な場合、統一的に対応できないので、それを使う処理ごとに対応しないといけないことになる。

Pythonのgetterやsetterのようなプロパティーへの配慮をするならRubyでもgetter、setterを生真面目に書くのが安全だと思う。

 

Ruby – クラス – getter/setter

概要

Rubyのインスタンス変数(プロパティー)は、Rubyのインスタンス変数(プロパティー)は、クラス内に隠蔽(encapsulate)されていて、外部からの参照・変更はできない。

アクセスメソッドを使うことで参照・変更が可能となるが、それだけだと全くオープンになるだけなので、不適切な値への対処などを体系的に行うことができない。

getter、setterは関数定義にひと手間かかるが、インスタンス変数の参照、代入時の処理が可能となる。以下は、そのコード例。

getter

クラス定義内でgetterを定義する場合、インスタンス変数の名前をそのままメソッド名にして、インスタンス変数の内容を返す。プロパティーを読みだす際に特段の処理が必要な場面はあまり想定されず、アクセスメソッドで公開するのと変わらないと思われる。

setter

関数名を[インスタンス変数名]=([引数])とすることで、セッターを定義できる。セッターの中で、インスタンス変数に値をセットする。

プロパティーへの代入の場合は、その有効性のチェックなどの処理が必要な場合も想定されるので、アクセスメソッドで単に自由に代入を許すよりも、セッターで必要な処理をさせる必要がないか意識すべきと考える。

 

Ruby – クラス~initialize

基本

Rubyのクラスのコンストラクターはinitialize()

initialize()ではなくインスタンスメソッド内でプロパティーを定義することもできる。

ただしこの場合には、プロパティーが定義されるメソッドをプロパティーが参照される前に読んでおかなければならない(そうしないと演算などで未定義でエラーになる)。

なので、普通はインスタンス生成時に自動的に呼ばれるinitialize()内でプロパティーを定義するのが安全。

オーバーロード不可

異なる引数のコンストラクターを意図してオーバーロードすることはできない。

上記のコードの場合、後の方で定義したコンストラクターは使える。

引数のパターンを変えたいときは、デフォルト引数可変長引数を用いるとよい。

 

Ruby – 関数

関数の定義

関数はdef...endで定義する。その関数が呼び出されるより前に定義されていなければならない。

関数の戻り値

return文で関数の戻り値を指定できる。

再帰呼び出し

再帰呼び出しにも対応。

デフォルト引数

あまりよくない方法

引数のデフォルト値をPythonと同じように指定できる。

実行時に指定した引数は、デフォルト引数があっても引数の順に入れ替えられる。以下の例の8行目で2つの仮引数に対して1つの実引数を与えているが、その値は1つ目の引数に使われている。

以下の6行目で仮引数名yzを指定して実引数を渡しているが、それらの値はxyに与えられてしまう。これは以下の流れによるものと考えられる。

  • 実行時に与えた引数は、デフォルト引数の有無にかかわらず先頭から順番に与えられる
  • 実行時に指定した引数'y=7''z=8'はそれぞれ式として評価され、その値が7、8となる
  • この2つの値が仮引数の先頭から与えられx=7y=8となり、zはデフォルト値の3が使われる

妥当な方法~キーワード引数

仮引数名を指定して実引数を渡す場合は、以下のようにキーワード引数を使うとよい。ただしキーワード引数で仮引数を指定した場合、実行時には指定を省略してデフォルト値を使うか、仮引数を明示して引数を与える必要がある。

デフォルト値を指定しない仮引数と、デフォルト値を指定したキーワード引数は混在して利用可能。

可変長引数

引数の頭に'*'をつけると配列とみなされ、複数の引数を渡せる。

配列の要素を使った処理の例。

通常の引数と可変長の引数が混在する場合、少なくとも通常の引数に対応する引数を渡す必要がある。

可変長引数の内容は空でもよく、まず通常の引数が埋められた後、その数を超える引数が可変長引数となる。

 

Ruby – 条件分岐

if~end節

基本形

一般的なif文の書き方で条件が真のときにthen節が実行される。必ずendで閉じる

else

else節も同じ。

elsif

elseifではなくてelsif

thenの省略

if節やelsif節の後のthenは省略可能。

一行で書く

一行にまとめることは可能。ただしthenがないとエラーになる。

';'で区切ると正常に実行される。

値を返す

if文は条件に対応した節の値を持つ。

後置

実行文の後にif文を置くと、条件が真の場合に文が実行される。

unless文

基本

unlessは条件が真でなければ(偽のときに)then節が実行される。

else

unless文のelse節は条件が真の時(偽でないとき)に実行される。

一行で書く

if文と同じく一行にまとめられる。

後置

実行文の後にunless文を置くと、条件が偽の場合に文が実行される。

case文

基本

case文は複数の条件を順番に確認していく。

thenの省略

thenは省略できる。

値を返す

case文は真となった条件の節の値が戻り値となる。

 

Ruby – 繰り返し処理

繰り返し処理の種類

for~要素を取り出して処理する

Rangeオブジェクトを使った繰り返し処理。".."演算子は両端の値を含む。

"..."演算子は終了値を含まない。

配列の内容を一つずつ取り出す例。

times~回数を指定する処理

timesは繰り返し回数を指定して実行。

繰り返しのカウンターを指定できる。カウンターはゼロからスタートし、指定回数-1までカウントされる。

upto/downto~カウントアップ/ダウン

uptoは初期値から終了値までを含んでカウントアップ。downtoは逆にカウントダウン。カウンターを省略しても可。

step~刻み幅を指定

[初期値].step([終了値], [刻み幅])で実行。

終了値がちょうどでない場合はその手前まで実行される。

ただし浮動小数点の計算誤差があり得るので、ちょっと気持ち悪い。

each~要素を一つずつ取り出す

eachforと同じくコレクションの要素を順に取り出す。

hashはキーと値を取り出せる。

ブロックの省略形{...}がある。

each_with_index~要素番号も取得

each_with_indexはコレクションから要素を取り出しながら、そのインデックスも得られる。Pythonのenumerateと同じ機能。

while~条件が真の間繰り返し

whileは条件が真の間ブロックを繰り返す。

whilebeginendブロックの後に置ける。上記の前置の場合は条件によってはブロックは一度も実行されないが、後置の場合は少なくとも1回は実行される。

until~条件が真になるまで繰り返し

untilwhileと逆で条件が偽の間繰り返し、真になったらループ終了。whileと同様、ブロックの前/後どちらに置いてもよい。

loop~無限ループ

loopは単にブロックを繰り返す。ループの終了判定が必要。

繰り返し制御

break~ループを終了

breakは実行中のループブロックを終了する。

next~残りを飛ばして続ける

nextはそれ以下の実行をせず、ループの次のステップに入る。他言語のcontinueと同じ。

以下の例ではn==2のときだけprintが実行されず、次のn=3の処理に入る。

redo~同じ条件で再実行

redoは現在のループと同じ条件で再度実行する(ループの次のステップではなく同じステップを再実行)。

この例では、n==2の時に"2 - C"を表示。その後n==3となってredoが実行され、a=="C"のままputsから実行、"3 - C"が表示されている。

次の例はすこしややこしい。

この例ではeachn=1とセットされて"1"が表示される。その後n==2となるのでredoが実行され、n==2のままprintが実行されて"2"が表示。その後n==3になるので次のeachに移り、n==2がセットされる。このn("2")を表示した後、n==3となるのでredoは実行されず・・・と続く。

 

Ruby – 変数と定数

変数の先頭はアルファベット小文字。任意のオブジェクト(の参照番地)を代入可能。

定数は先頭がアルファベット大文字。定数に値を代入しようとするとwarning。

 

Ruby – 演算子

算術演算子

単項演算子

算術演算子と括弧

除算は被除数・除数とも整数の時は小さい側の整数に丸められる(floor)。いずれかあるいは両方が実数の時は実数になる。

'%'で剰余を計算。

'**'でべき乗を計算。

文字列演算子

結合

'+'演算子は2つの文字列の結合結果を返し、元の文字列は保持される。

'<<'演算子は2つの文字列の結合結果を返し、1つ目の変数を結合結果で書き換える。

'*'演算子は文字列を繰り返して複製して結合。

ビット演算子

'~'は前値の否定演算子。

’&’’|’'^'はそれぞれAND、OR、XOR。

'<<''>>'は指定した数だけシフトする演算子。

数値比較演算子

数値の比較演算子は標準的な定義。

文字列比較演算子

論理演算子

'!'は前値の否定演算子。

'&&''||'は論理積、論理和。

'and''or'も論理積、論理和だが挙動が異なる。

andor演算子は他の殆どの演算・処理に対して優先度が低いため、演算子より前の項がまず実行されてから2項目との評価が行われるため。括弧で優先順位を明示すると&&||と同じ結果になる。

三項演算子

三項演算子の形。

使用例。