Ruby

a == 1 && a == 2 && a == 3がtrueになるRubyスクリプト

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が実行されてることがわかります、順番的には以下のような感じです。

  1. a == 1aが呼ばれる
  2. オブジェクトがかえる
  3. object == 1でオブジェクトの==メソッドが呼ばれる
  4. 引数の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

わかりやすいですね :smile:
このスクリプトなら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