ようへいの日々精進XP

よかろうもん

2018 年 05 月 29 日 (火)

天気

  • 曇り後晴れ

ジョギング

日課

  • (腕立て x 50 + 腹筋 x 50) x 3

今日のるびぃ ~ REx - Ruby Examination にチャレンジ (12) ~

REx - Ruby Examination の問題を自分なりにアレンジした上で 1 〜 3 問くらいずつ解いていく. 正直言ってかなり難しい. 尚, irb に動作確認環境は以下の通り.

$ ruby --version
ruby 2.1.10p492 (2016-04-01 revision 54464) [x86_64-linux]
$ irb --version
irb 0.9.6(09/06/30)

module_eval

以下のコードを実行するとどうなるか.

module Mod
  B = 100

  def method
    1000
  end
end

Mod.module_eval do
  def self.method
    p B
  end
end

B = 10000

Mod.method

10000 が出力される

以下, irb にて確認.

irb(main):001:0> module Mod
irb(main):002:1>   B = 100
irb(main):003:1> 
irb(main):004:1*   def method
irb(main):005:2>     1000
irb(main):006:2>   end
irb(main):007:1> end
=> :method
irb(main):008:0> 
irb(main):009:0* Mod.module_eval do
irb(main):010:1*   def self.method
irb(main):011:2>     p B
irb(main):012:2>   end
irb(main):013:1> end
=> :method
irb(main):014:0> 
irb(main):015:0* B = 10000
=> 10000
irb(main):016:0> 
irb(main):017:0* Mod.method
10000
=> 10000

以下, 解説より抜粋.

  • module_eval にブロックを渡した際のネスト状態は [] となり, メソッドはトップレベルに定義されることになる
  • トップレベルで定数を定義した場合は Object クラスの定数になる
  • 設問では, self.method はブロックで渡されている為, トップレベルの定数を参照することになり Mod.method10000 を返す
irb(main):001:0> module A; end
=> nil
irb(main):002:0> A.module_eval do
irb(main):003:1*   p Module.nesting
irb(main):004:1> end
[]
=> []
irb(main):005:0> A.module_eval %Q{
irb(main):006:0"   p Module.nesting
irb(main):007:0" }
[A]
=> [A]
irb(main):008:0> A.module_eval do
irb(main):009:1*   p Module.nesting
irb(main):010:1> end
irb(main):011:0> B = "Hello, world"
=> "Hello, world"
irb(main):012:0> p Object.const_get(:B)
"Hello, world"
=> "Hello, world"

なるほどー, ブロックで渡した時と文字列で渡した時の違いってこれかなー.

ちなみに, class_eval でも同様の事が言える.

irb(main):001:0> class C; end
=> nil
irb(main):002:0> C.class_eval do
irb(main):003:1*   p Module.nesting
irb(main):004:1> end
[]
=> []
irb(main):009:0> C.class_eval %Q{
irb(main):010:0"   p Module.nesting
irb(main):011:0" }
[C]
=> [C]

ということで, 以下のドキュメントの意味がシュッと頭に入ってくるような気がする.

  • 文字列で定義した場合には, クラス又はモジュール定義式内の定数, クラス変数を参照する
  • ブロックで定義した場合には, クラス又はモジュール定義外の定数, クラス変数を参照する

一応, 以下, ドキュメントより抜粋.

  • module_eval に文字列を渡した場合の定数及びクラス変数のスコープはモジュール定義式内と同じスコープとなる
  • 対して, module_eval にブロックを渡した場合は, 定数とクラス変数のスコープはブロックの外側のスコープとなる
  • 尚, ローカル変数に関しては, class_eval 及び module_eval の外側と共有する
irb(main):001:0> class C
irb(main):002:1> end
=> nil
irb(main):003:0> a = 1
=> 1
irb(main):004:0> C.class_eval %Q{
irb(main):005:0"   def m
irb(main):006:0"     return :m, #{a}
irb(main):007:0"   end
irb(main):008:0" }
=> :m
irb(main):009:0> 
irb(main):010:0* p C.new.m
[:m, 1]
=> [:m, 1]

フムフム...