Open60

メタプロRuby読む

ハガユウキハガユウキ
irb(main):012:0> Greeting.new("hello")
=> #<Greeting:0x0000000111c3b628 @text="hello">
irb(main):013:0> obj = Greeting.new("hello")
=> #<Greeting:0x00000001126f2d50 @text="hello">
irb(main):014:0> obj.welcome
=> "hello"
irb(main):015:0> obj.instance_variables
=> [:@text]
irb(main):016:0> obj.class
=> Greeting
irb(main):017:0> obj.class.instance_methods
irb(main):018:0> obj.class.instance_methods(false)
=> [:welcome]
irb(main):019:0>
ハガユウキハガユウキ

プログラム実行時に新しいインスタンスメソッドを追加できるのか?

ハガユウキハガユウキ

メタプログラミングとは、言語要素を実行時に操作するコードを記述することである

この本において言語要素とは、変数、クラス、メソッドなどの言語の要素のこと。

ハガユウキハガユウキ

もっとライトな定義があってそっちの方がわかりやすいかも

メタプログラミングとは、コードを記述するコードを記述することである。

ハガユウキハガユウキ

言語要素を操作したいなら、言語要素は実行時に存在しなければいけない。

ハガユウキハガユウキ

C言語においては、プログラムは「言語要素が存在するコンパイル時の世界」と「マシンコードが存在する実行時の世界」の2つの異なる世界にまたがっている。

ハガユウキハガユウキ

Rubyのコーディングの能力を上げる以上、ライブラリとは付き合えた方が良くて、そうなるとどうしてもメタプロとは付き合う必要がある。

ハガユウキハガユウキ
irb(main):001* 3.times do
irb(main):002*   class C
irb(main):003*     puts "hoge"
irb(main):004*   end
irb(main):005> end
hoge
hoge
hoge
=> 3
irb(main):006> C
=> C
irb(main):007> C.class
=> Class
irb(main):008> C.instance_methods

irb(main):009> C.instance_methods(false)
=> []
irb(main):010* class C
irb(main):011*   def x
irb(main):012*     puts "hoge"
irb(main):013*   end
irb(main):014> end
=> :x
irb(main):015> C.instance_methods(false)
=> [:x]
irb(main):016>

classは同じ名前のクラスを再度定義しない。再オープンしてメソッドを追加しているのがわかる。

ハガユウキハガユウキ
irb(main):001> # encoding: utf-8
irb(main):002>
irb(main):003* class Numeric
irb(main):004*   def to_money(currency = nil)
irb(main):005*     Monetize.from_numeric(self, currency || Money.default_currency)
irb(main):006*   end
irb(main):007> end
irb(main):008>
=> :to_money
irb(main):009> require "monetize"
=> true
irb(main):010> 12.to_money("USD")
[WARNING] The default rounding mode will change from `ROUND_HALF_EVEN` to `ROUND_HALF_UP` in the next major release. Set it explicitly using `Money.rounding_mode=` to avoid potential problems.
=> #<Money fractional:1200 currency:USD>
irb(main):011> standard_price = 12.to_money("USD")
=> #<Money fractional:1200 currency:USD>
irb(main):012> standard_price.class.instance_methods(false)
=>
[:as_us_dollar,
 :as_ca_dollar,
 :as_euro,
 :split,
 :fractional,
 :thousands_separator,
 :round_to_nearest_cash_value,
 :decimal_mark,
 :bank,
 :cents,
 :to_s,
 :symbol,
 :to_i,
 :to_f,
 :round,
 :format,
 :allocate,
 :hash,
 :inspect,
 :to_money,
 :currency,
 :dollars,
 :currency_as_string=,
 :currency_as_string,
 :with_currency,
 :dup_with,
 :exchange_to,
 :to_d,
 :amount]
irb(main):
irb(main):001> 12.to_money("USD")
(irb):1:in `<main>': undefined method `to_money' for an instance of Integer (NoMethodError)

12.to_money("USD")
  ^^^^^^^^^
Did you mean?  to_enum
	from <internal:kernel>:187:in `loop'
	from /Users/yuuki_haga/.rbenv/versions/3.3.1/lib/ruby/gems/3.3.0/gems/irb-1.14.1/exe/irb:9:in `<top (required)>'
	from /Users/yuuki_haga/.rbenv/versions/3.3.1/bin/irb:25:in `load'
	from /Users/yuuki_haga/.rbenv/versions/3.3.1/bin/irb:25:in `<main>'
irb(main):002>
ハガユウキハガユウキ
irb(main):004> String.methods.grep /^instance/
=>
[:instance_method,
 :instance_methods,
 :instance_variables,
 :instance_variable_get,
 :instance_variable_set,
 :instance_variable_defined?,
 :instance_of?,
 :instance_eval,
 :instance_exec]
irb(main):005>

こんな感じでクラスのメソッド探せるのは知らんかった。地味に便利

ハガユウキハガユウキ
irb(main):007> 1.class.instance_methods(false).grep /^instance/
=> []
irb(main):008> 1.class.instance_methods(false).grep /^z/
=> [:zero?]

こんなのもできる

ハガユウキハガユウキ

当初はモンキーパッチは、ルールを無視して実行時にこっそりとコードを変更することから、ゲリラパッチと呼ばれていた。これらのパッチを複数当てると、時折直感に反するような相互作用が生まれることがあり、Zope 2では、交戦中のパッチと呼ばれていた。

モンキーパッチは実行時にコードを変更することなのか。

https://ja.wikipedia.org/wiki/モンキーパッチ

ハガユウキハガユウキ

オープンクラスは、実行時に既存クラスをオープンして機能追加できる機能で、
モンキーパッチは、実行時にコードを変更すること

ハガユウキハガユウキ

インスタンス変数は値が代入された時に初めて出現する

irb(main):012* class Hoge
irb(main):013*   def x
irb(main):014*     @y = 1
irb(main):015*   end
irb(main):016> end
=> :x
irb(main):017> hoge = Hoge.new
=> #<Hoge:0x0000000102dd8788>
irb(main):018> hoge.instance_variables
=> []
irb(main):019> hoge.x
=> 1
irb(main):020> hoge.instance_variables
=> [:@y]
irb(main):021>

当たり前っちゃ当たり前だけど、言葉にされると確かになと思う。

ハガユウキハガユウキ

同じメソッドであっても、クラスに着目しているときは、インスタンスメソッドと呼び、オブジェクトに注目しているときは、メソッドと呼ぶ

これわかりやすい。クラスについて話をしているなら、インスタンスメソッドと読んだ方が良いし、食らうから生成されたオブジェクトについての話をしているならメソッドと読んだ方が良い。

irb(main):022> String.instance_methods == "abc".methods
=> true
irb(main):023> String.methods == "abc".methods
=> false
irb(main):024>
ハガユウキハガユウキ

インスタンスにはインスタンス変数とクラスへの参照しかなくて、メソッドの実態は持ってなくて、メソッドの実態はクラスが持っているって言われて、なんとなくわかるんだけど、実際のコード見てないからまだ腑に落ちてない

ハガユウキハガユウキ

クラスのメソッドは、Classクラスのインスタンスメソッド。
ややこしいけど、確かにそう

ハガユウキハガユウキ

ArrayクラスはObjectクラスを継承しているので、「配列はオブジェクトである」と言える。
Objectクラスはあらゆるオブジェクトで便利に使えるメソッドを持っている(to_s等)
BasicObjectクラスは、Rubyのクラス階層のルートであり、基本的なメソッドをいくつか持ったクラスである。

irb(main):025> Array.superclass
=> Object
irb(main):026> Object.superclass
=> BasicObject
irb(main):027> BasicObject.superclass
=> nil
irb(main):028>
ハガユウキハガユウキ

こうなっているのか
Rubyにおいては、クラスはモジュールである。
インスタンスの生成や継承を使うときはクラスを選択して、どこかでインクルードするときはモジュールを選択すれなど、目的に応じて使い分ければ、意図が明確になって良い。


irb(main):028> Class.superclass
=> Module
irb(main):029> Module.superclass
=> Object
irb(main):030>
ハガユウキハガユウキ

さっき作ったHogeクラスもオブジェクトなのか。

irb(main):012* class Hoge
irb(main):013*   def x
irb(main):014*     @y = 1
irb(main):015*   end
irb(main):016> end
irb(main):031> Hoge.superclass
=> Object
irb(main):032>
ハガユウキハガユウキ

クラスが継承しているクラスと、そのクラスがどのクラスのインスタンスかは全く別の話だから、そこごっちゃにしない方が良いな。
クラスAから生成されたクラスB(オブジェクト)は、クラスAを継承していないくて、そこ関係ないってこと。

ハガユウキハガユウキ

オープンクラスは便利な側面もあるが、反対に、いろんなライブラリを使った開発をしていると、クラスが衝突して、意図しない動作をするプログラムになる恐れがある。
そのため、名前空間をクラスに導入した。

ハガユウキハガユウキ

そのスコープにおける定数探索のパスを、このように取得できたのか。

=> Class
irb(main):039* module M
irb(main):040*   class C
irb(main):041*     module M2
irb(main):042*       puts Module.nesting
irb(main):043*     end
irb(main):044*   end
irb(main):045> end
M::C::M2
M::C
M
=> nil
irb(main):046>
ハガユウキハガユウキ
irb(main):060* module M
irb(main):061*   T = "abdM"
irb(main):062*   class C
irb(main):063*     T = "abdC"
irb(main):064*     module M2
irb(main):065*       T = "abdM2"
irb(main):066*       puts T
irb(main):067*     end
irb(main):068*   end
irb(main):069> end
abdM2

irb(main):001* module M
irb(main):002*   T = "abdM"
irb(main):003*   class C
irb(main):004*     T = "abdC"
irb(main):005*     module M2
irb(main):006*       puts T
irb(main):007*     end
irb(main):008*   end
irb(main):009> end
abdC
=> nil
irb(main):010>

irb(main):001* module M
irb(main):002*   T = "abdM"
irb(main):003*   class C
irb(main):004*     module M2
irb(main):005*       puts T
irb(main):006*     end
irb(main):007*   end
irb(main):008> end
abdM
=> nil
irb(main):009>

irb(main):001> T = "hogehoge"
=> "hogehoge"
irb(main):002* module M
irb(main):003*   class C
irb(main):004*     module M2
irb(main):005*       puts T
irb(main):006*     end
irb(main):007*   end
irb(main):008> end
hogehoge
=> nil
ハガユウキハガユウキ

classクラスのクラスはClassなのか

irb(main):009> Class.class
=> Class
irb(main):010>
ハガユウキハガユウキ

メソッドを呼び出すと、Rubyは以下の2つのことを行う。

  1. メソッドを探す。これはメソッド探索と呼ばれる。
  2. メソッドを実行する。これにはselfと呼ばれるものが必要だ。

このプロセス(メソッドを探して実行する)は、すべてのオブジェクト指向言語で起きている。

なるほど

ハガユウキハガユウキ

メソッド探索を一言でまとめると「Rubyがレシーバのクラスに入り、メソッドを見つけるまで継承チェーンを上ること」である。

なるほど

ハガユウキハガユウキ

メソッド探索は、「右へ一歩、それから上へ」

多くの人がこのように図を描くので、この動きは「右へ一歩、それから上へ(one step to the right, then up)」ルールと呼ばれる。レシーバのクラスに向かって右へ一歩進み、メソッドが見つかるまで継承チェーンを上へ進むわけだ。

これわかりやすい。

ハガユウキハガユウキ

MyClassはスーパークラスを指定していないので、デフォルトのスーパークラスであるObjectを継承していることになる。

これはわかる。何も継承してないと、スーパークラスがObjectだった。

継承チェーンにはmoduleも含まれる。
ancestorsメソッドを使えば、継承チェーンを見れる。

irb(main):015> 1.class.ancestors
=> [Integer, Numeric, Comparable, Object, PP::ObjectMixin, Kernel, BasicObject]
irb(main):016>

irb(main):018> Array.ancestors
=> [Array, Enumerable, Object, PP::ObjectMixin, Kernel, BasicObject]
irb(main):019>
ハガユウキハガユウキ

モジュールをクラスに(あるいは別のモジュールに)インクルードすると、Rubyはモジュールを継承チェーンに挿入するのだ。それはインクルードするクラスの真上に入る。

これは知らんかった。

irb(main):019* module M1
irb(main):020*   def my_method
irb(main):021*     "M1#my_methos()"
irb(main):022*   end
irb(main):023> end
=> :my_method
irb(main):024* class C
irb(main):025*   include M1
irb(main):026> end
=> C
irb(main):027> class D < C; end
=> nil
irb(main):028> D.ancestors
=> [D, C, M1, Object, JSON::Ext::Generator::GeneratorMethods::Object, PP::ObjectMixin, Kernel, BasicObject]
irb(main):029>
ハガユウキハガユウキ

モジュールに対しても、オープンクラスのような機能が提供されているのか

irb(main):030* module A
irb(main):031*   def x
irb(main):032*     "hogex"
irb(main):033*   end
irb(main):034> end
=> :x

irb(main):038* class C
irb(main):039*   include A
irb(main):040> end

irb(main):042> C.new.x
=> "hogex"
irb(main):043* module A
irb(main):044*   def x
irb(main):045*     "hogehogey"
irb(main):046*   end
irb(main):047> end
=> :x
irb(main):048> C.new.x
=> "hogehogey"
irb(main):049>
irb(main):050>
ハガユウキハガユウキ

Objectクラスは、Kernelモジュールをincludeしているので、すべての継承チェーンでKernelモジュールが挿入されている。

ハガユウキハガユウキ

Rubyのコードは常にオブジェクトの内部で実行されるので、Kernelモジュールのメソッドはどこからでも呼び出せる。おかげでメソッドのprintが言語のキーワードのように見えるわけだ。

コードが常にオブジェクトの内部で実行されるのは知らんかったし、実感できない。

irb(main):050> Kernel.private_instance_methods.grep /^pr/
=> [:printf, :print, :proc]
irb(main):051> print "hoge"
hoge=> nil
irb(main):052>
ハガユウキハガユウキ

Rubyのコードはオブジェクト(カレントオブジェクト)の内部で実行される。カレントオブジェクトはselfとも呼ばれる。selfキーワードでアクセスできるからだ。

カレントオブジェクトって言い方初めてきいた。
selfでアクセスできるのは知ってる。

ハガユウキハガユウキ

selfの役割を担えるオブジェクトは同時に複数は存在しない。

これも実感できないから、そうなんだって感じだな。

ハガユウキハガユウキ

Rubyのプログラムを開始すると、Rubyのインタプリタが作ったmainと呼ばれるオブジェクトの内部にいることになる。このオブジェクトはトップレベルコンテキストと呼ばれることもある。コールスタックがトップレベルにある時のオブジェクトだからだ。ここで言うトップレベルとは、メソッドを呼び出していない時、あるいは呼び出したメソッドがすべて戻ってきた時の状態だ

プログラム実行時は、mainオブジェクトの内部にいることになるのか(だからselfでmainを指している)。面白い。

irb
irb(main):001> self
=> main
irb(main):002> self.class
=> Object
irb(main):003> self.class.ancestors
=> [Object, PP::ObjectMixin, Kernel, BasicObject]
irb(main):004>
ハガユウキハガユウキ

モジュールをincludeする順番によって、メソッド呼び出しが意図した挙動にならない時がありそうだから、そこは気をつけた方がよさそう

irb(main):005> module A; end
=> nil
irb(main):006> module B; end
=> nil
irb(main):007* class C
irb(main):008*   include A
irb(main):009*   include B
irb(main):010> end
=> C
irb(main):011> C.ancestors
=> [C, B, A, Object, PP::ObjectMixin, Kernel, BasicObject]
irb(main):012>

include Bが先に継承チェーンで呼ばれる。
リネームするか、includeの順番変えるかだな。

ハガユウキハガユウキ

メソッドを呼び出すということは、オブジェクトにメッセージを送っているということなんだ

わかるようでわからない。

ハガユウキハガユウキ

> Pryのオブジェクトは、インタプリタの設定を自身のアトリビュートとして保持している。

これは知らなかった。

ハガユウキハガユウキ

メソッドは動的に呼び出すだけではなくて、動的に定義することもできる。
Module#define_methodを使えば、メソッドをその場で定義できる。
メソッド名とブロックを渡す必要があり、ブロックがメソッドの本体となる。
実行時にメソッドを定義するこの方法を、動的メソッドと呼ぶ

irb(main):012* class MyClass
irb(main):013*   define_method :my_method do |my_args|
irb(main):014*     my_args * 3
irb(main):015*   end
irb(main):016> end
=> :my_method
irb(main):017> obj = MyClass.new
=> #<MyClass:0x0000000125ddd828>
irb(main):018> obj.my_method(2)
=> 6
irb(main):019> obj.my_method("hg")
=> "hghghg"
irb(main):020>
ハガユウキハガユウキ

シンボルは「何かの名前」に使われることがほとんどである。特にメソッド名などのメタプログラミングに関係する名前だ。シンボルが名前に適しているのは、文字列にある文字と違ってイミュータブル(変更不能)だからである。メソッド名が途中で変更されることはないはずだ。したがって、メソッド名を参照するときにはシンボルを使うことになる。

なるほど

ハガユウキハガユウキ

メソッド探索でメソッドが見つからなかったら、レシーバのmethod_missingメソッドを呼び出す。
method_missingメソッドはBasicObjctにprivateインスタンスメソッドとして定義されている。

    irb(main):042> BasicObject.private_instance_methods.grep /^method/
=> [:method_missing]
irb(main):043> BasicObject.new
=> #<BasicObject:0x0000000125db3cd0>
irb(main):044> BasicObject.new.method_missing
(irb):44:in `<main>': private method `method_missing' called for an instance of BasicObject (NoMethodError)

irb(main):067* class HG
irb(main):068*   def me_m
irb(main):069*     method_missing(:joffjo)
irb(main):070*   end
irb(main):071> end
=> :me_m
irb(main):072> HG.new.me_m
(irb):69:in `me_m': undefined method `joffjo' for an instance of HG (NoMethodError)

    method_missing(:joffjo)
    ^^^^^^^^^^^^^^
	from (irb):72:in `<main>'
	from <internal:kernel>:187:in `loop'
	from /Users/yuuki_haga/.rbenv/versions/3.3.1/lib/ruby/gems/3.3.0/gems/irb-1.14.1/exe/irb:9:in `<top (required)>'
	from /Users/yuuki_haga/.rbenv/versions/3.3.1/bin/irb:25:in `load'
	from /Users/yuuki_haga/.rbenv/versions/3.3.1/bin/irb:25:in `<main>'
irb(main):073> 1.length
(irb):73:in `<main>': undefined method `length' for an instance of Integer (NoMethodError)

1.length
 ^^^^^^^
	from <internal:kernel>:187:in `loop'
	from /Users/yuuki_haga/.rbenv/versions/3.3.1/lib/ruby/gems/3.3.0/gems/irb-1.14.1/exe/irb:9:in `<top (required)>'
	from /Users/yuuki_haga/.rbenv/versions/3.3.1/bin/irb:25:in `load'
	from /Users/yuuki_haga/.rbenv/versions/3.3.1/bin/irb:25:in `<main>'

確かにできた

ハガユウキハガユウキ

send使えば簡単に呼び出せた

irb(main):081> HG.new.send(:method_missing, :fuga)
(irb):81:in `<main>': undefined method `fuga' for an instance of HG (NoMethodError)

HG.new.send(:method_missing, :fuga)
      ^^^^^
	from <internal:kernel>:187:in `loop'
	from /Users/yuuki_haga/.rbenv/versions/3.3.1/lib/ruby/gems/3.3.0/gems/irb-1.14.1/exe/irb:9:in `<top (required)>'
	from /Users/yuuki_haga/.rbenv/versions/3.3.1/bin/irb:25:in `load'
	from /Users/yuuki_haga/.rbenv/versions/3.3.1/bin/irb:25:in `<main>'
irb(main):082>
ハガユウキハガユウキ

ゴーストメソッドとは、レシーバ側には対応するメソッドが見た当たらないが、呼び出した際に通常の呼び出しのように呼べるメソッドのこと。
method_missingをオーバーライドして、missingの時の振る舞いを書いておけば、ゴーストメソッドを実現できる。
ライブラリ作る時で、同じようなメソッドをたくさん定義しないといけない場面で、使うとよさそうではあるが、仕事で使うのはやめといた方が良い。

ハガユウキハガユウキ

継承先にメソッドが定義されていると、ゴーストメソッドに辿り着かないから、そこは注意。

クラスを生成したクラスが持っているインスタンスメソッドが、そのクラス(オブジェクト)のメソッドとなる。

ハガユウキハガユウキ

最小限のメソッドしかない状態のクラスをブランクスレートと呼ぶ

ブランクスレートクラスを継承すれば、ゴーストメソッドが呼ばれない問題も解決するのか

ハガユウキハガユウキ
irb(main):102* class HG
irb(main):103*   def me_m
irb(main):104*     method_missing(:joffjo)
irb(main):105*   end
irb(main):106*
irb(main):107*   def method_missing(name)
irb(main):108*     "ghostmethods_#{name}"
irb(main):109*   end
irb(main):110> end
irb(main):111> HG.new.method_missing("apple")
=> "ghostmethods_apple"
irb(main):112> HG.new.apple
=> "ghostmethods_apple"
irb(main):113> HG.new.display
#<HG:0x000000012656c5f8>=> nil
irb(main):114> HG.new.class.ancestors
=> [HG, Object, JSON::Ext::Generator::GeneratorMethods::Object, PP::ObjectMixin, Kernel, BasicObject]
irb(main):116> Kernel.instance_methods.grep /^method/
=> [:methods, :method]
irb(main):117> self
=> main
irb(main):118> self.class
=> Object
irb(main):119> self.class.ancestors
=> [Object, JSON::Ext::Generator::GeneratorMethods::Object, PP::ObjectMixin, Kernel, BasicObject]
irb(main):121> method(:display).owner
=> Kernel
irb(main):128> HG.new.methods.grep /^apple/
=> []
irb(main):129>
irb(main):130> HG.new.display
#<HG:0x00000001263e5ea0>=> nil
irb(main):131> HG.new.meron
=> "ghostmethods_meron"
irb(main):132>
irb(main):133> Kernel.instance_methods.grep /^method/
=> [:methods, :method]
irb(main):134> method(:display).owner
=> Kernel
irb(main):135>
irb(main):137> Object.instance_methods.grep /^method/
=> [:methods, :method]
irb(main):138> Object.instance_methods.grep /^display/
=> [:display]
irb(main):139>
irb(main):141> self.class.ancestors
=> [Object, JSON::Ext::Generator::GeneratorMethods::Object, PP::ObjectMixin, Kernel, BasicObject]

ゴーストメソッドを作ってみた
displayって名前を使うと、ゴーストメソッドが呼ばれなかった。
理由としては、Objectクラスにdisplayメソッドが定義されていて、メソッド探索時にそちらが見つかって呼ばれていたため。

ハガユウキハガユウキ

module Kernel
全てのクラスから参照できるメソッドを定義しているモジュール。 Object クラスはこのモジュールをインクルードしています。

https://docs.ruby-lang.org/ja/latest/class/Kernel.html

これ初めて知った

instance method Method#owner
このメソッドが定義されている class か module を返します。

https://docs.ruby-lang.org/ja/latest/method/Method/i/owner.html

これ意外と使える

instance method Object#method
オブジェクトのメソッド name をオブジェクト化した Method オブジェクトを返します。

ownerと組み合わせたら使える。
https://docs.ruby-lang.org/ja/latest/method/Object/i/method.html

ハガユウキハガユウキ

ブロックは任意の引数をyieldから受け取れる。
ブロックはメソッドを呼び出す時にだけ定義できる。
ブロックはメソッドに渡され、メソッドはyieldキーワードを使ってブロックをコールバックする。
Kernel#block_given?でブロックの有無を確認できる

jsのコールバック関数に似ているな。

irb(main):152* def abc(d)
irb(main):153*   return yield if block_given?
irb(main):154*   d
irb(main):155> end
=> :abc
irb(main):156> abc(1)
=> 1
irb(main):157> abc(2)
=> 2
irb(main):158> abc(1) { 5 }
=> 5
irb(main):161* def abc(d)
irb(main):162*   return yield(d) if block_given?
irb(main):163*   d
irb(main):164> end
=> :abc
irb(main):165> abc(2) { |x| x * 9 }
=> 18
irb(main):166> abc(2)
=> 2
irb(main):167> abc(2) { |x| "hoge_#{x}" }
=> "hoge_2"
irb(main):168>

メソッドの振る舞いをこちらが意図したものに変更できるのがメリットではあるか。

ハガユウキハガユウキ

ブロックはメソッド実行時のコンテキストを覚えていて、メソッド内でブロックを実行する時は、メソッド実行時のコンテキストを見る。

irb(main):174* def abc(d)
irb(main):175*   x = "hello"
irb(main):176*   yield(d)
irb(main):177> end
=> :abc
irb(main):178> x = "HELLO"
=> "HELLO"
irb(main):179> abc(5) { |d| "#{x}_#{d}" }
=> "HELLO_5"
irb(main):180>
ハガユウキハガユウキ

Kernel#local_variablesで、そのスコープで定義された変数が見れるのか。

irb(main):181> local_variables
=> [:x, :obj, :_]
irb(main):182>
ハガユウキハガユウキ

ブロックを使うことで、変数をスコープを超えて参照したりできたりするのか