Ruby 3.4 リファレンスマニュアル

Ruby プログラムの実行

[edit]

Ruby プログラム

Ruby プログラムの実行は文の連なりの評価です。なんらかの形であたえられたプログラムテキストをコンパイルし、BEGIN 文があればそれを評価し、トップレベルの式の連なりを評価し、END ブロックがあれば最後にそれを評価して終了します (終了処理の詳細については 終了処理 を参照のこと)。

if

if 文は、まず条件式を評価し、その値が真ならば対応する本体を評価します。偽ならば elsif 節の条件式を順番に評価し、その値が始めて真になった節の本体を評価します。それらがすべて偽なら else 節の本体を評価します。

文全体の値は最後に実行した本体の値です。ただし評価された本体に式がなかった場合、あるいはすべての条件式が偽でかつ else 節もなかった場合は nil です。

if 修飾子

右辺の条件式を評価し、真であれば左辺の式を評価してその値を返します。偽であれば左辺は評価されず、nil を返します。詳細は 制御構造/if を参照してください。

unless 修飾子

右辺の条件式を評価し、偽であれば左辺の式を評価してその値を返します。真であれば左辺は評価されず、nil を返します。詳細は 制御構造/unless を参照してください。

while

本体を評価する前に毎回条件式を評価する前判定の繰り返しです。条件式が最初から偽であれば本体は一度も評価されません。文全体の値は nil です(引数を伴った break で抜けた場合はその値になります)。詳細は 制御構造/while を参照してください。

until

while と条件の真偽が逆になるだけで、評価順序(前判定)と文の値は while と同様です。詳細は 制御構造/until を参照してください。

while 修飾子

左辺が begin 〜 end 式以外であれば while 文と同様の前判定になりますが、左辺が begin 〜 end 式である場合は本体を先に一度評価してから条件式を評価する後判定になります。文の値は while 文と同様です。詳細は 制御構造/while を参照してください。

until 修飾子

while 修飾子と条件の真偽が逆になるだけで、begin 〜 end 式に対する後判定の扱いを含め評価順序は同様です。詳細は 制御構造/until を参照してください。

for

in の後の式を評価して得たオブジェクトに対して each メソッドを呼び出し、その要素を左辺の変数へ代入しながら本体を繰り返し評価します。文全体の値は each メソッドの戻り値です。ブロック付きメソッド呼び出しとは異なり、for は新しいローカル変数のスコープを導入しないため、ループ変数や本体内で代入した変数はループの前後で同じスコープに残ります。詳細は 制御構造/for を参照してください。

begin 〜 end

本体の式を上から順に評価し、最後に評価した式の値を返します(式がなければ nil)。rescue・else・ensure 節を伴う場合の評価順序と値については 制御構造/begin を参照してください。

クラス定義式

クラスを定義します。評価は(コンパイル時ではなく)実行時に行われます。

書式
class ClassName [< スーパークラス式]
  式
end

クラス定義式は評価されるとまずクラスを生成しようとします。スーパークラス式が指定されていたらそれを評価し、その値を上位クラスとする Class クラスのインスタンスを生成します。式が省略されていたら Object を上位クラスとします。

一方、もし同名のクラスがすでにある場合はそれを使います。そのときスーパークラス式が指定されており、その結果と得たクラスのスーパークラスが (equal? において) 違う場合は例外 TypeError が発生します。

クラスを得たら次にそれを定数「ClassName」に代入します。これによってクラス名が決定されます。このとき同名の定数に Class のインスタンスでないものが代入されている場合は例外 TypeError が発生します。

最後に新しいフレームを生成し、トップレベルブロックの self および class に定義を行おうとするクラスを設定して、そのフレーム上で定義式中の式を評価します。クラス定義式は最後に評価した式の結果を返します。最後に評価した式が値を返さない場合は nil を返します。

つまり Ruby では何度も「クラス定義の追加」をすることが可能です。

モジュール定義式

モジュールを定義します。評価は(コンパイル時ではなく)実行時に行われます。

書式
module ModuleName
  本体
end

モジュール定義式は評価されるとまず新しい無名のモジュールを生成します。ただしすでに ModuleName と名付けられたモジュールがある場合はそれをかわりに使います。このような場合は「モジュール定義の追加」をすることになります。

モジュールを得たら次にそれを定数 ModuleName に代入します。この定数がモジュールの名前になります。このとき同名の定数にモジュール以外が代入されていた場合は例外 TypeError が発生します。

最後に、新しいフレームを生成し、そのトップレベルブロックの self および class にモジュール ModuleName を設定し、そのフレーム上で定義式中の式を評価します。モジュール定義式は最後に評価した式の結果を返します。最後に評価した式が値を返さない場合は nil を返します。

特異クラス定義式

オブジェクトの特異クラスを定義します。評価は(コンパイル時ではなく)実行時に行われます。

書式
class << EXPR
  本体
end

まず特異クラスを定義しようとするオブジェクトの式 EXPR を評価します。続いてそのオブジェクトの特異クラスを(まだ生成されていなければ)生成します。最後に、新しいフレームを生成し、トップレベルブロックの self および class に生成した特異クラスを設定し、そのフレーム上で定義式中の式を評価します。

特異クラス定義式は、最後に評価した式の結果を返します。評価する式がひとつもなければ nil になります。

ただし Fixnum Symbol のインスタンスおよび true false nil には特異クラスは定義できません。

メソッド定義式

メソッドを定義します。評価は(コンパイル時ではなく)実行時に行われます。

書式
def method_name(arg, argwithdefault=expr, *restarg, &block)
  本体
end

評価すると、実行中のブロックの class にメソッド本体を当該の名前で定義します。もしすでにその class 自体に同名のメソッドが定義されている場合は、古いメソッドを捨てて新しいメソッドの内容によって定義しなおします。

メソッド定義式は、メソッド名を Symbol にしたオブジェクトを返します。

特異メソッド定義式

オブジェクトの特異クラスにメソッドを定義します。評価は(コンパイル時ではなく)実行時に行われます。

書式
def expr.method_name(arg, argwithdefault=expr, *restarg, &block)
  本体
end

まず最初に式 expr を評価します。続いてその値のオブジェクトの特異クラスを (まだ作成されていなければ) 作成します。最後に、その特異クラスにメソッド method_name を定義します。

特異メソッド定義式は、メソッド名を Symbol にしたオブジェクトを返します。

ただし Fixnum Symbol のインスタンスおよび true false nil には特異メソッドは定義できません。

BEGIN

BEGIN ブロックはコンパイル時に登録され、プログラムの実行の最初に評価されます(制御構造/BEGIN を参照)。

END

END ブロックは BEGIN と異なり実行時に登録され(登録する文が実行されなかった場合は登録されません)、プログラムの実行の最後に評価されます(制御構造/END を参照)。

メソッド

メソッドの呼び出し

まずレシーバ式を評価してレシーバとなるオブジェクトを得ます。レシーバ式が省略された場合は呼び出しを行っているブロックのself がレシーバです。

続いて引数式を左から右の順番で評価し、レシーバに対してメソッドの検索を行います。検索が失敗したら例外 NameError を発生、成功したらメソッドを実行します。

またメソッドを実行する際にはブロックを与えることが可能です。メソッドに対してブロックを与えると、そのメソッドの実行中にyield が実行されたときにはじめてブロックが評価されます。yield されなかったときはブロックは単に無視され、実行されません。

メソッドにブロックを与える場合、そのブロックはメソッドを呼び出す側のブロックの self と class を継承します。Module#module_eval/class_eval、 BasicObject#instance_eval の三つだけが例外で、以下のように変更されます。

Module#module_eval, Module#class_eval

self、class ともそのレシーバ

BasicObject#instance_eval

self がそのレシーバ、class がそのレシーバの特異クラス

eval

eval の第二引数に Proc オブジェクトまたは Binding オブジェクトを与えたときは、その生成時のブロックのうえで文字列を評価します。

メソッドの実行

メソッドの実行はフレームのうえにひとつだけブロックがある状態で開始します。このブロックを以下、仮にトップレベルブロックと呼んでおきます。トップレベルブロックの self はレシーバで、class は定義されていません。

まず、省略不可能な引数が存在するなら、与えられた値をトップレベルブロックのローカル変数に代入します。

省略可能な引数が存在し、実際に省略されていたら、トップレベルブロック上でデフォルト式を評価し、その値をトップレベルブロックのローカル変数に代入します。実際には省略されなかったら、与えられた値を同じくローカル変数に代入します。

*args の形のパラメータ指定があるなら、残りの引数すべてを配列としてローカル変数に代入します。

さらに、ブロック引数 blockvar が存在するならば、メソッドに与えられたブロックを Proc オブジェクト化してトップレベルブロック上のローカル変数 blockvar に代入します。ブロックが与えられていないなら nil を代入します。

続いて本体が評価され、メソッドレベルの rescue 節または else 節が評価され、最後に ensure 節が評価されます。

メソッド全体の値は return に渡した値です。 return が呼び出されなかった場合は、本体/rescue/else の中で最後に評価した式の値です。その三つともすべてがカラだった場合は nil になります。

ブロック付きメソッド呼び出し

メソッドに対してブロックが与えられていたらそのメソッドをブロック付きメソッドと呼びます。ブロック付きメソッドからは yield によって与えられたブロックに実行を移すことができます。

ブロックは引数をとることができます。実行開始時の扱いは後述の「ローカル変数」の項(ブロック引数は多重代入とみなされる)を参照してください。

break … ブロックがスタックフレーム上にあるなら、そのフレームのブロックの直後にジャンプします。ブロック付きメソッドを break して終了したらその値は nil です。スタックフレーム上にないなら例外 LocalJumpError を発生します。

next … ブロックの実行を終了します。yield 式の値は next の引数の値(指定しなければ nil)になります(制御構造/next)。

retry はブロックの制御とは無関係で、現在の Ruby ではブロック中を含め rescue 節以外で retry を用いると例外 SyntaxError が発生するため、rescue 節の中でのみ有効です(制御構造/retry)。

eval, instance_eval, module_eval

Kernel.#eval にコンテキスト(ProcBinding)を渡す場合と同様に、BasicObject#instance_evalModule#module_evalModule#class_eval もブロックだけでなく文字列を渡して評価できます。いずれの場合も self(module_eval/class_eval では class も)がレシーバに応じて変わる一方、呼び出し元のローカル変数はブロックでも文字列でも参照できます(ただし文字列の評価中に新たに定義したローカル変数は外側には残りません。また module_eval/class_eval では定数・クラス変数のスコープが文字列かブロックかで異なります)。詳細は各メソッドのドキュメント(BasicObject#instance_evalModule#module_evalModule#class_eval)を参照してください。

代入

代入とは、変数・定数のいずれかにオブジェクトを記憶させることを言います。 []= や属性代入のメソッド呼び出しも文法上は代入のように見えますが、それはここで定義する代入ではありません。

変数には、何回でも、どんなオブジェクトでも代入できます。定数にも同様にあらゆるオブジェクトを代入できますが、ただ一回しか代入できません。つまりいったんオブジェクトを代入したらあとから記憶するオブジェクトを変更することはできません。これは記憶したオブジェクトそれ自身が変化することを禁止するのではないことに注意してください。

多重代入

多重代入では、まず左辺の各要素のうち []= による代入(配列参照)や 属性= による代入(属性参照)になっているものについて、代入先のレシーバおよび添字の式を左から右の順に評価します。続いて右辺の各式を左から右の順に評価します。最後に、それらの評価結果を用いて左から右の順に代入を行います。この評価順序は、単一の代入(左辺を評価してから右辺を評価する)と一貫するように Ruby 3.1 で変更されました([bug#4443])。

多重代入の文法や代入される値の対応関係については 演算子式/多重代入 を参照してください。

変数と定数

変数および定数を評価すると、それが記憶しているオブジェクトを返します。この操作を「変数(定数)の参照」と言います。未定義の状態で参照した場合の挙動は種類によって異なり、ローカル変数は self への(引数のない)メソッド呼び出しとして解釈され(メソッドの探索にも失敗すれば例外 NameError)、インスタンス変数とグローバル変数は nil を返し、クラス変数と定数は例外 NameError が発生します。

種類ごとの代入・参照の記法やスコープの詳細は 変数と定数 を参照してください。以下、実行(評価)の観点から補足します。

ローカル変数

ローカル変数はただひとつのブロックに所属します。ブロックとはコードのある範囲に対応する実行時の構造で、ネストが可能です。具体的にはブロック付きメソッド呼び出しや eval 系メソッドの実行にともなって生成されます。ローカル変数は、所属するブロックおよびそのブロックの上にネストされたブロックの中からのみ代入・参照できます。

またさらにブロックは特定の「フレーム」のうえに積まれ、そこに所属します。別フレームのうえにあるローカル変数を参照することはできません。フレームとは以下の文の実行開始時に生成される実行時の構造です。

フレームが生成されると自動的にブロックもひとつ積まれますので、これらの文の本体でもすぐにローカル変数を使うことができます。

ローカル変数の定義は、コンパイル時にそのフレーム中で定義されていないローカル変数への代入をプログラムテキスト中に書くことによって行います。所属するブロックは代入が書かれている一番外側のブロックです。このことからもわかるように、ローカル変数の定義はコンパイル時にすべて完了します (eval 系メソッドは実行中にコンパイルをしていることに注意してください)。定義された変数の初期値は nil です。

ローカル変数の定義および参照にあたっては、外側のブロックから順番に変数を探します。この結果としてローカル変数のネスト (shadowing) はできません。ただし上下関係にない複数のブロックに別々のローカル変数が存在することはありえます。

呼び出しブロックの実行にあたっては、ブロックが引数をとることができますが、これは実行しようとするブロック上での多重代入とみなされます。たとえば以下のコードのブロック実行開始時には、

some_iterator do |a,b|
  # ....
end

次のような操作がまず実行されます。

a, b = <some_iterator から yield された値>

ローカル変数の記法や基本的なスコープについては 変数と定数/ローカル変数 を参照してください。

インスタンス変数

変数と定数/インスタンス変数 を参照してください。 Object#remove_instance_variable でインスタンス変数を削除できます。

クラス変数

変数と定数/クラス変数 を参照してください。

グローバル変数

変数と定数/グローバル変数 を参照してください。

定数

定数への代入はメソッド以外のすべてで可能で、最初の代入が定義を兼ねます。定数が所属するクラスは代入が行われたブロックの class です。また非常に特殊な例外としてメソッド Module#const_set によっても定義が可能です。さらに Module#remove_const を使うことで定義の取り消しが可能です。

すでに定義されている定数の再定義および代入はできません。実際には警告のみで代入が可能ですが、これは一時的な避難措置であり本来の仕様ではありません。従ってこれに依存したコードはできるだけ書かないようにすべきです。

定数の参照可能な範囲や探索順序については 変数と定数/定数参照の優先順位 を参照してください。