a == 1 && a == 2 && a == 3がtrueになるRubyスクリプト見て面白そうだったので書きました
==
が呼ばれたタイミングでレシーバーを変えてやり直す
require 'continuation'
def a
callcc do |c|
Object.new.tap do |o|
o.define_singleton_method(:==) do |other|
c.call(other)
end
end
end
end
puts a == 1 && a == 2 && a == 3
# true
ぱっと見で動作が分かりづらいと思いますが==
を確認するコードを差し込むとちゃんと1 == 1 && 2 == 2 && 3 == 3
が実行されてることがわかります、順番的には以下のような感じです。
-
a == 1
のa
が呼ばれる - オブジェクトがかえる
-
object == 1
でオブジェクトの==
メソッドが呼ばれる - 引数の1を
a
の返り値として継続(a == 1
の== 1
部分)を実行する、a
の返り値が1になっているので1 == 1
が実行されtrue
がかえる
require 'continuation'
def a
callcc do |c|
Object.new.tap do |o|
o.define_singleton_method(:==) do |other|
# 確認用
puts "object == #{other}"
c.call(other)
end
end
end
end
# 確認用
using Module.new { refine(Integer) { def ==(o); puts "#{self} == #{o}"; super; end } }
puts a == 1 && a == 2 && a == 3
# object == 1
# 1 == 1
# object == 2
# 2 == 2
# object == 3
# 3 == 3
# true
わかりやすいですね
このスクリプトならa == 4 && a == 5 && a == 6
と続けてもtrue
が返ります、便利!
a
の==
メソッドをRefinementsで上書き
a.class
のメソッドを上書きするのでa
にどんな値が来ても動きます。Refinementsを使って上書きするとこのスクリプトの外ではtrue
にならないので便利ですね。
a = :hi
using Module.new { refine(a.class) { def ==(*); true; end } }
puts a == 1 && a == 2 && a == 3
# true
呼ばれるたびa
が返す値をかえる
順番よく値を返していくならFiberかEnumerator
を使うとべんりです!
f = Fiber.new do
Fiber.yield 1
Fiber.yield 2
Fiber.yield 3
end
define_method(:a) do
f.resume
end
puts a == 1 && a == 2 && a == 3
# true
e = Enumerator.new { |y| y << 1 << 2 << 3 }
define_method(:a) do
e.next
end
puts a == 1 && a == 2 && a == 3
# true