目次
- 目次
- 実行環境など
- スコープ, ブロック
- クラス, モジュール, メソッド
Enumerable
- リテラル
- その他
実行環境など
以下のコードは全て Ruby 2.2.2p95 で動作確認をしている。
罫線で区切られたソースコードより下の部分は、 標準出力とエラー出力を合わせた出力である。
なお、 Ruby では標準出力はデフォルトでバッファリングされるが、 バッファリングされない場合の出力を記載している。
したがって、 標準出力とエラー出力が混ざるようなコードに対しては、 実際の出力内容とこのページに書かれた出力内容とで行の順番が異なる可能性がある。
$stdout.sync = true
などを前もって実行しておくなどして、 バッファリングを無効化しておくと良い。
スコープ, ブロック
束縛, スコープ
オブジェクトに名前が与えらている状態を 「束縛」 という。
例えば、 variable = 9
というコードを実行すると、 9
というオブジェクトに variable
という名前を与えているという束縛ができ上がる。
この束縛が共有される範囲を 「スコープ」 という。
a = 7
b = "abcd"
c = Array.new(3)
p a
p c
class Foo
p c
end
上の例では、 初めの 3 行で 3 つの束縛が定義される。
4 行目で、 a
という名前を束縛するオブジェクトである 7
を表示している。
5 行目では、 c
に対して同じことを行っている。
a
と c
の束縛はその前の行で定義されているから、 ここまでは束縛が共有されていることになる。
しかし、 7 行目で c
を束縛するオブジェクトを表示しようとするとエラーになる。
これは、 7 行目の実行時点では c
に関する束縛が存在しないからで、 最初の 5 行でが存在していたはずの束縛がここでは共有されていないことになる。
すなわち、 スコープが異なるということになる。
スコープが変化するのは、 モジュール定義 (クラス定義を含む) とメソッドである。
このようなスコープが変化する場所を 「スコープゲート」 という。
この言葉を用いて言い換えれば、 Ruby のスコープゲートは module
, class
, def
の 3 つである。
a = 1
class Test
b = 2
p [:in_class, local_variables]
def foo
c = 3
p [:in_method, local_variables]
end
end
test = Test.new
test.foo
p [:in_main, local_variables]
上のソースコードには、 トップレベルのスコープ, Test
クラス内のスコープ, foo
メソッド内のスコープの 3 つのスコープが存在する。
スコープが異なるので、 それぞれのスコープで定義された束縛 (ローカル変数の定義) は別のスコープでは共有されない。
上のように、 ローカル変数の束縛の有無は Kernel#local_variables
で確認できる。
フラットスコープ
スコープゲートをメソッド呼び出しに置き換えれば、 スコープが新しく作られないので変数を共有できる。 これをうまく使うと、 一部のメソッドのみで共有されるローカル変数を作ることができる。
class Test
def self.define
shared = 0
define_method(:display) do
p shared
end
define_method(:plus_one) do
shared += 1
end
end
def foo
p shared
end
end
Test.define
test = Test.new
test.display
test.plus_one
test.display
test.foo
ブロック
Ruby のブロックは、 コードと束縛のペアである。 ブロックは、 それが定義された時点での束縛を保持し、 ブロックが実行されるときはその保持した束縛のもとでコードが実行される。
def foo(&block)
x = 5
block.call
end
x = 3
foo do
p x
end
上のソースコードでは、 6 行目の do
から 8 行目の end
までがブロックである。
このブロックが定義された時点では、 3
というオブジェクトに x
というローカル変数が束縛されている。
foo
メソッドが 6 行目で呼ばれると、 メソッド定義内の 2 行目以降のコードが、 新しいスコープで実行される。
ここでは、 2
に x
というローカル変数が束縛されているが、 block.call
でブロックを実行すると、 ブロックが保持している束縛をもとにブロックの内容である p x
が実行されるので、 上のような出力が得られるのである。
proc, lambda
ブロックはオブジェクトではないが、 オブジェクトに変換することはできる。
このときにできるのが Proc
クラスのインスタンスである。
ブロックから Proc
インスタンスを得るには、 Proc.new
, Kernel#proc
, Kernel#lambda
, 矢印演算子の 4 つの方法がある。
Proc.new
と Kernel#proc
はどちらを使っても同じインスタンスを返し、 Kernel#lambda
と矢印演算子の 2 つも同じだが、 例えば Proc.new
を使って生成したものと Kernel#lambda
を使って生成したものは微妙に違う。
Proc.new
や Kernel#proc
を用いたものを 「proc」 と呼び、 Kernel#lambda
や矢印演算子を用いたものを 「lambda」 と呼ぶ。
proc と lambda の違いは全てを説明しようとすると非常に複雑だが、 主に 2 つである。
まず、 return
の挙動が異なる。
proc 内で return
を呼ぶと proc が定義されているスコープから戻ろうとするが、 lambda で return
を呼ぶと単に lambda から戻るだけである。
def proc_test
block = proc {return 1}
block.call
return 0
end
def lambda_test
block = lambda {return 1}
block.call
return 0
end
p proc_test
p lambda_test
また、 proc より lambda の方が引数の扱いに厳しく、 lambda に対して定義と異なる引数の数で呼びだそうとするとエラーが生じる。 ただし、 これには特殊ケースがいろいろあり、 実際は複雑である。
なお、 proc や lambda はどちらもブロックをオブジェクト化しただけなので、 保持している束縛はブロックと同じように定義時点の束縛である。 同じスコープであっても、 proc や lambda が定義された後で定義された束縛は共有されていない。
lambda = lambda {p x}
x = 3
lambda.call
Method
, UnboundMethod
Method
, UnboundMethod
メソッドはオブジェクトではないが、 ブロックと同じようにオブジェクトに変換することはできる。
これは Object#method
により行われ、 Method
インスタンスが得られる。
Method
インスタンスはブロックとは異なり、 定義時のスコープではなく、 生成するときにレシーバに指定したオブジェクトのスコープで評価される。
class Test
def initialize
@variable = 0
end
def foo
p @variable
end
end
test = Test.new
foo = test.method(:foo)
foo.call
上のようにして得られた Method
インスタンスに対して Method#unbind
を呼ぶか、 Module#instance_method
を用いるかすると、 UnboundMethod
インスタンスが得られる。
これは、 束縛をもたないメソッドのようなものである。
そのままでは実行できないが、 UnboundMethod#bind
を用いて適当なオブジェクトに束縛すれば実行できるようになる。
このとき当然だが、 束縛するオブジェクトはもとのクラスと同じクラスかそのサブクラスのインスタンスである必要がある。
もともとがモジュールにあったメソッドであれば、 このような制限はない。
class Test
def initialize
@variable = 0
end
def foo
p @variable
end
end
foo = Test.instance_method(:foo)
test = Test.new
test.instance_eval do
@variable = 5
end
bounded_foo = foo.bind(test)
bounded_foo.call
UnboundMethod
インスタンスは Module#define_method
の第 2 引数に渡すことができる。
module Test
def foo
puts("foo")
end
end
foo = Test.instance_method(:foo)
String.send(:define_method, :foo, foo)
"String".foo
Method
インスタンスの呼び出しは、 lambda の呼び出しと同じように引数の個数に厳密である。
引数の数が異なるとエラーになる。
Binding
Binding
Ruby ではほとんど全てのものがオブジェクトなので、 束縛もオブジェクトにすることができる。
束縛は Binding
インスタンスで管理する。
Kernel#binding
により、 このメソッドが呼び出されたスコープ全体の束縛を保持した Binding
インスタンスが取得できる。
Binding
インスタンスは Kernel#eval
の第 2 引数に指定でき、 第 1 引数の文字列をその Binding
インスタンスが保持する束縛のもとで評価される。
x = 5
test_binding = binding
y = 3
eval("p x", test_binding)
eval("p y", test_binding)
上の例からも分かるが、 Binding
インスタンスが保持するのはスコープ全体の束縛である。
したがって、 同じスコープ内であればどこで Binding
インスタンスを生成しても同じものが得られる。
Binding
インスタンスは Proc#binding
を用いても生成できる。
これはレシーバが定義されているスコープ全体の束縛を保持した Binding
インスタンスを返す。
ブロックが保持する束縛は定義時に存在していたものだけなので、 この違いには注意すること。
block = -> {p x}
x = 3
block_binding = block.binding
eval("p x", block_binding)
block.call
組み込み定数である TOPLEVEL_BINDING
にはトップレベルのスコープの Binding
インスタンスが格納されている。
これを使うと、 ソースコード上のどの位置でもトップレベルにアクセスできる。
class Test
def foo
eval("p self", TOPLEVEL_BINDING)
end
end
Test.new.foo
クラス, モジュール, メソッド
基本
Ruby には 「クラスのようなもの」 としてクラスとモジュールがある。
Ruby ではほとんど全てのものがオブジェクトなので、 当然クラスそのものやモジュールそのものもオブジェクトである。
クラスは Class
クラスのインスタンスで、 モジュールは Module
クラスのインスタンスである。
クラスやモジュールは、 どちらもインスタンスメソッドをもつことができるという点で、 通常のオブジェクトと異なる。
クラスは、 インスタンスの生成とスーパークラスの参照という 2 つの機能をモジュールに追加したものである。
逆に言えば、 モジュールは、 インスタンスの生成ができずスーパークラスをもたないクラスだと考えられる。
実際、 Class
は Module
のサブクラスで、 Class
で新しく定義されているインスタンスメソッドは new
, allocate
, superclass
の 3 つだけである。
特異クラス
オブジェクトは全て特異クラスをもつ。
特異クラスは継承ができず、 オブジェクトの特異メソッドはこの特異クラスに定義される。
特異クラスは Object#singleton_class
で取得できる。
普通のオブジェクトの特異クラスのスーパークラスはそのオブジェクトのクラスである。
string = "abc"
p string.class
p string.singleton_class
p string.singleton_class.superclass
クラスの特異クラスのスーパークラスはそのクラスのスーパークラスの特異クラスである。
p String.superclass
p String.singleton_class
p String.singleton_class.superclass
p String.superclass.singleton_class
クラスでないモジュールに関しては、 普通のオブジェクト同じような挙動を示す。
p Kernel.class
p Kernel.singleton_class
p Kernel.singleton_class.superclass
継承チェーンへのモジュールの追加
Module#include
は、 継承チェーンにおいてレシーバとなるモジュールの上に引数のモジュールを追加する。
逆に、 Module#prepend
は、 モジュールの下に引数のモジュールを追加する。
module M
end
module N
end
class C
prepend M
include N
end
class D < C
end
p D.ancestors
Kernel
モジュールのインスタンスメソッドがどこでもレシーバなしで呼び出せるのは、 Object
が Kernel
をインクルードしているためである。
異なるモジュールを複数インクルードした場合は、 順にモジュールのすぐ上にモジュールが追加されていく。 プリペンドも同様である。
module M
end
module N
end
class C
include M
include N
end
p C.ancestors
モジュールのインクルードやプリペンドの際に、 すでに継承チェーンに属しているモジュールが再び継承チェーンに追加されることはない。
module M
end
module N
include M
end
class O
prepend M
include N
end
p O.ancestors
p N.ancestors
上の例では、 O
での継承チェーンは M
, O
, N
, M
と続くはずだが、 プログラムの 3 行目で行われるはずの M
の継承チェーンへの追加は、 すでに継承チェーンに M
があるため行われない。
一方、 N
での継承チェーンにおいては、 3 行目での M
の追加は 1 回目なので行われる。
Object#extend
を用いることで、 レシーバの特異メソッドに引数のモジュールのインスタンスメソッドを追加できる。
module Extension
def foo
puts("foo")
end
end
object = Object.new
object.extend(Extension)
object.foo
クラスメソッドはクラスの特異メソッドだから、 同様にしてクラスメソッドも定義できる。
module Extension
def foo
puts("foo")
end
end
class ExtendedClass
extend Extension
end
ExtendedClass.foo
クラスの特異メソッドはクラスの特異クラスのインスタンスメソッドであるから、 上のソースコードは以下と等価である。
module Extension
def foo
puts("foo")
end
end
class ExtendedClass
class << self
include Extension
end
end
ExtendedClass.foo
メソッド探索
メソッド探索において注意すべきパターンを挙げておく。 基本は、 まずレシーバの特異クラスを探し、 そこになかったらスーパークラスを順にたどって (継承チェーンを上に向かって) 探していく。 特異クラスにメソッドはないことが多いので、 メソッド探索はレシーバの普通の意味でのクラスから始まると考えることもある。
クラスにモジュールがプリペンドされている場合は、 少し注意が必要である。
module PrependedModule
def foo
puts("foo in PrependedModule")
end
end
class Test
prepend PrependedModule
def foo
puts("foo in Test")
end
end
Test.new.foo
Test
インスタンスに対して foo
メソッドを呼ぼうとすると、 メソッド探索は Test
からではなくその下にある PrependedModule
から始まる。
どんなときでも継承チェーンの一番下からメソッド探索が行われると考えれば良い。
以下は、 同名のインスタンスメソッドをもつ複数のモジュールをインクルードしている例である。
module ModuleAlpha
def foo
puts("foo in ModuleAlpha")
end
def bar
foo
end
end
module ModuleBeta
def foo
puts("foo in ModuleBeta")
end
end
class Test
include ModuleAlpha
include ModuleBeta
end
p Test.ancestors
test = Test.new
test.bar
Test.new.test
が呼ばれた時点で、 self
が test
に変わり、 メソッド探索が始まる。
まず、 bar
メソッドを探索する。
これは test
のクラスである Test
にないので、 次に ModuleBeta
を探すがここにもないので、 ModuleAlpha
を探してここで見つかるので、 ModuleAlpha#bar
が実行される。
このメソッドの内部では foo
メソッドが呼ばれているが、 レシーバがないので self
に対するメソッド呼び出しだと解釈されて self.foo
と同様の挙動を示す。
すなわち、 ここでは test.foo
と書かれているのと同じように動く。
したがって、 再びメソッド探索が開始する。
test
のクラスである Test
には foo
はないので、 ModuleBeta
を探しここで見つかるので、 ModuleBeta#foo
が実行される。
結果的に、 上で示した出力を得る。
ソースコードだけを見ると、 ModuleAlpha#bar
が呼ばれると同じモジュールに定義されている ModuleAlpha#foo
が呼ばれそうだが、 実際はそうではない。
カレントオブジェクト
Ruby のコードは全てカレントオブジェクト (self
) のもとで実行される。
レシーバを指定せずにメソッドを呼び出すと、 レシーバはカレントオブジェクトであると解釈される。
また、 インスタンス変数への参照は、 その変数がカレントオブジェクトに属していると解釈される。
カレントオブジェクトが変更される場所はいくつかある。
まず、 レシーバを指定してメソッドを呼び出すと、 そのメソッドを実行している間は self
がそのレシーバになる。
class Test
def foo
@x = 10
p [:foo, self]
bar
end
def bar
@x += 1
p [:bar, self]
end
end
test = Test.new
test.foo
モジュール定義 (クラス定義を含む) の内部では、 self
はそのモジュールそのものになる。
ただし、 メソッド定義の内部では、 上で述べたようにメソッドのレシーバが self
である。
class Test
p [:in_class, self]
def foo
p [:in_method, self]
end
end
Test.new.foo
上で述べたようなものと同じ機能をもつメソッドのブロック内でも self
が変化する。
例えば、 クラス定義に相当する Class.new
のブロック内ではその返り値が self
になるし、 Module#define_method
のブロック内ではメソッドのレシーバが self
になる。
カレントモジュール
Ruby ではカレントモジュールというものもある。 メソッドを定義すると、 カレントモジュールのインスタンスメソッドになる。
トップレベルでは、 カレントモジュールは Object
である。
モジュール定義 (クラス定義を含む) の内部では、 そのモジュールがカレントモジュールになる。
メソッドの内部では、 カレントオブジェクトのクラスがカレントモジュールになる。
eval 系メソッド
BasicObject#instance_eval
は、 レシーバのコンテキストで与えられたブロックを評価する。
すなわち、 与えたブロック内でのカレントオブジェクトをレシーバ自身にし、 カレントモジュールをレシーバの特異クラスにする。
特に前者の効果により、 レシーバのインスタンス変数や private なメソッドなどにアクセスができる。
class Test
def initialize
@variable = 0
end
private def foo
puts("foo")
end
end
x = 5
test = Test.new
test.instance_eval do
foo
p @variable
@variable = x
end
test.instance_eval do
p @variable
end
Module#class_eval
は、 レシーバのクラスのコンテキストで与えられたブロックを評価する。
すなわち、 与えられたブロック内でのカレントオブジェクトとカレントモジュールをともにレシーバ自身にする。
なお、 BasicObject#instance_eval
, Module#class_eval
のそれぞれに対して、 ブロックに引数を与えることができる BasicObject#instance_exec
, Module#class_exec
というものもある。
アクセス制御
Ruby のメソッドの可視性には、 private, protected, public の 3 種類が存在する。
private なメソッドはレシーバをつけて呼び出すことができなくなる。
すなわち、 そのメソッドが self
に対して呼び出されるものであるようなスコープでしか呼び出せなくなる。
class Foo
private def foo
puts("foo called")
end
def bar
foo
end
end
Foo.new.bar
Foo.new.foo
private メソッドのルールはあくまで 「レシーバをつけて呼び出せない」 であるので、 例えば明示的に self
に対して呼び出そうとするとエラーになる。
class Foo
private def foo
puts("foo called")
end
def bar
self.foo
end
end
Foo.new.bar
また、 スーパークラスから継承したメソッドはレシーバなしで呼べるので、 そのメソッドが private であってもサブクラスから呼び出せる。
class SuperFoo
private def foo
puts("foo called")
end
end
class Foo < SuperFoo
def bar
foo
end
end
Foo.new.bar
なお、 アクセサメソッド (def hoge=(value)
で定義できるもの) はレシーバとして self
だけは許される。
レシーバなしの呼び出しのみ可能にすると、 変数への代入と区別したいときに困るからだろう。
protected なメソッドは、 そのメソッドをもつオブジェクトが self
であるような場所でのみ呼び出せる。
private との違いは例えば以下のコードで分かる。
class Foo
private def private
puts("private called")
end
protected def protected
puts("protected called")
end
def bar
protected
private
Foo.new.protected
Foo.new.private
end
end
foo = Foo.new
foo.bar
このコードでは、 foo.bar
を実行する段階で、 9 行目から始まる bar
メソッドの中身を実行し始めるが、 ここでの self
は foo
であるから、 foo
がもつ protected なメソッドが呼び出せる。
Foo.new.private
がエラーになるのはすでに述べた通りである。
Object#send
を用いると、 可視性に関係なくメソッドを呼び出せる。
class Foo
private def private
puts("private called")
end
protected def protected
puts("protected called")
end
end
Foo.new.send(:private)
Foo.new.send(:protected)
メソッドの可視性の設定は、 Module#private
, Module#protected
, Module#public
を用いる。
これらを引数なしで呼び出すと、 それ以降で定義されるメソッドがその可視性に設定される。
シンボルか文字列を引数に渡す (複数指定できる) と、 その名前をもつメソッドがその可視性に設定される。
なお、 Ruby 2.1.0 からはメソッド定義式がその名前のシンボルを返すようになったので、 上のように private def foo
と書ける。
Enumerable
Enumerable
Enumerable#chunk
Enumerable#chunk
Enumerable#chunk
は、 要素を初めから順にブロックで評価し、 評価値が同じになる部分をグループ化してできる Enumerator
インスタンスを返す。
返される Enumerator
インスタンスは、 ブロックの評価値とその評価値になるもとの要素からなる配列のペアを順に回す。
array = [2, 6, 7, 9, 1, 4, 1, 7]
array.chunk{|s| s.even?}.each do |element|
p element
end
ブロックの評価値が nil
か :_separator
である場合は、 その要素は返される Enumerator
インスタンスに含まれなくなる。
この前後でブロックは区切られる。
例えば、 ファイルを読み込んで 1 行ずつ分割したいが、 ハイフン 5 つからなる行でさらに分割したい場合に、 以下のように書ける。
separated = DATA.chunk do |line|
next (line != "-----\n") || nil
end
separated.each do |_, data|
pp data
end
__END__
AAAAA
AAAAA
AAAAA
-----
-----
BBBBB
BBBBB
BB
-----
CCCCC
ブロックの評価値が :_alone
である場合は、 その要素は単独のブロックをなす。
separated = DATA.chunk do |line|
next (line != "-----\n") || :_alone
end
separated.each do |_, data|
pp data
end
__END__
AAAAA
AAAAA
AAAAA
-----
-----
BBBBB
BBBBB
BB
-----
CCCCC
Enumerable#find
の 第 1 引数
Enumerable#find
の 第 1 引数
Enumerable#find
はブロックを評価して最初に真になる要素を返すが、 引数を指定することで真になる要素が見つからなかったときの返り値を指定できる。
このときの引数は、 単なる値ではなく Proc
インスタンス (call
メソッドをもっていれば他のオブジェクトでも良い) である。
単に call
を呼び出すだけで、 引数は与えない。
array = [2, 6, 7, 9, 1, 4, 1, 7]
proc = -> do
next :none
end
p array.find(proc){|s| s % 5 == 0}
ブロックなしの Enumerable
のメソッド
Enumerable
のメソッド
ブロックなしの Enumerable#map
は Enumerator
インスタンスを返す。
繰り返しのできるオブジェクト (何でも良い) を Enumerator
インスタンスに変換するという認識で良いのだろうか…。
DATA.map.with_index do |element, i|
p [i, element]
end
__END__
foo
hoge
hugahuga
bar
上のコードの DATA.map.with_index
を DATA.with_index
にすることはできない。
これは、 DATA
が File
インスタンス (File
は Enumerable
をミックスインしている) であるため、 Enumerator
クラスで定義される with_index
メソッドが呼べないためである。
Enumerable#map
に限らず、 ブロックをとる Enumerable
のメソッドは、 たいていブロックを指定しないと対応する Enumerator
インスタンスを返すようになっている。
例えば、 おもしろくない例だが、 以下のような感じである。
array = [2, 7, 1, 8, 2, 8]
array.find.each_with_index do |element, i|
p [i, element]
end
リテラル
% 記法
% 記法は %Q
, %q
, %s
, %r
, %W
, %w
, %x
の 7 種類で、 %Q
と %q
は文字列、 %s
はシンボル、 %r
は正規表現、 %W
と %w
は配列、 %x
はコマンド出力を表す。
大文字版と小文字版があるものは、 式展開やバックスラッシュ記法の有無が異なる。
大文字の方は式展開などが行われ (文字列リテラルのダブルクォートに相当)、 小文字の方は式展開などが行われない (文字列リテラルのシングルクォートに相当)。
また、 %r
では式展開だけが行われ、 バックスラッシュ記法は無効である。
アルファベットを省略した場合は %Q
であると解釈される。
p %|A'B'C/D"E"F\u0061=#{2*2}|
p %Q{A'B'C/D"E"F\u0061=#{2*2}}
p %q[A'B'C/D"E"F\u0061=#{2*2}]
p %s$A'B'C/D"E"F\u0061=#{2*2}$
p %r!A'B'C/D"E"F\u0061=#{2*2}!
p %W$A'B' C/D "E"F \u0061=#{2*2}$
p %w$A'B' C/D "E"F \u0061=#{2*2}$
p %x*echo hello*
%W
と %w
はスペースで要素が区切られるが、 %W
において式展開が含まれる場合はその評価を行う前に要素に区切られる。
string = "X Y"
p %W(A\ B #{string}C\sD #{3 * 3})
ヒアドキュメント
<<
に続けて、 クォートで括られた何らかの識別子を書くと、 それの次の行からその識別子だけの行の直前の行までがリテラルになる。
ダブルクォートを利用した場合は式展開やエスケープが有効になり、 シングルクォートを利用した場合は無効になる。
識別子をクォートで括らなかった場合は、 ダブルクォートで囲った場合と同じになる。
p <"EOB"
String Line 1
String Line 2
EOB
p <EOS
String Line 1
String Line 2
EOS
p <`HELLO`
echo hello
HELLO
ヒアドキュメントの終了を示す行は識別子だけからなる必要がある。
識別子の前後にスペースを入れることはできない上に、 コメントも書くことができない。
<
の代わりに <-
にすると、 ヒアドキュメントの終了を表す識別子をインデントすることができるようになるが、 依然としてコメントは書けない。
p <"EOB"
String Line 1
String Line 2
EOB # comment
String Line 3
EOB
p <-"EOB"
String Line 1
String Line 2
EOB
文字列リテラルと違い、 ヒアドキュメントの中ではクォートをエスケープする必要がない。
特にシングルクォートによるヒアドキュメントは、 完全にソースコードに書かれているそのままの文字列になる。
以下の例の "
, '
, \
の挙動について特に注目すること。
p <"EOB"
"AB" 'CD' \"AB\" \'CD\' #{3 * 5}
EOB
p <'EOB'
"AB" 'CD' \"AB\" \'CD\' #{3 * 5}
EOB
<"EOB"
などの部分が式になるので、 以下のような記述が可能になる。
def method(foo, string, bar)
p string
end
method(1, <"EOB", 2)
This is a here document.
line 2
line 3
EOB
1 行に複数のヒアドキュメントを書くことも可能である。
def method(first_string, second_string)
p first_string
p second_string
end
method(<FIRST, <SECOND)
first line 1
first line 2
FIRST
second line 1
second line 2
SECOND
条件式としての範囲式
範囲式は Ruby ではよく使われるが、 実は if などの条件式を書くところでも範囲式を使うことができ、 このときは Range
インスタンスになるのではなく特殊な振る舞いをする。
ドット 2 つの範囲式 (a..b
の形式) の挙動は以下の通りである。
まず初期状態として、 1 つ目の式 (上の例では a
) が評価され、 これが真を返すまでは範囲式全体は false を返す。
1 つ目の式が真を返すと全体で true を返す。
このとき、 2 つ目の式も真ならば初期状態に戻る。
これ以降は 2 つ目の式のみを評価し、 2 つ目の式が真になるまで全体で true を返す。
2 つ目式が真になったら全体で true を返し、 その後に初期状態に戻る。
(1..20).each do |i|
if (i % 5 == 0)..(i % 3 == 0)
puts(i)
end
end
ドット 3 つの範囲式 (a...b
の形式) の場合は、 1 つ目の式が真を返したときに 2 つ目の式が真を返しても初期状態に戻らないところが異なる。
(1..20).each do |i|
if (i % 5 == 0)...(i % 3 == 0)
puts(i)
end
end
以下は while 文で範囲式を用いた例である。
i = 0
while (i % 3 == 0)...(i % 5 == 0)
puts(i)
i += 1
end
その他
Kernel#require
, Kernel#load
Kernel#require
, Kernel#load
Kernel#require
と Kernel#load
は外部ファイルを読み込むためのメソッドである。
これらは、 第 1 引数に指定された名前のファイルをロードパスから探し、 それをトップレベルで実行する。
したがって、 例えば、 読み込んだファイルで定数 (クラス名を含む) が定義されていればトップレベルの定数になる。
ただし、 読み込んだファイルのローカル変数は共有されない。
一方、 この 2 つのメソッドは、 様々な点で異なった挙動を示す。
まず、 Kernel#require
はライブラリを読み込むときに用いるもので、 Kernel#load
はファイルを読み込むときに用いるとされていて、 意味合いが異なる。
また、 Kernel#require
は拡張子を省略できるが、 Kernel#load
はできない。
さらに、 Kernel#require
は同じファイルは 1 度だけしか実行しないが、 Kernel#load
は同じファイルであってもこのメソッドが呼ばれれば何度でも実行する。
なお、 Kernel#load
の第 2 引数に true を指定すると、 定数を読み込まなくなる。
より具体的には、 読み込むファイルを実行するときに無名のモジュールが作成され、 ファイル内の定数はこのモジュールのもとに定義され、 ファイルの実行が終わったらこのモジュールは破棄される。