LoginSignup
13
2

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-01-23

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
13
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
2