Hatena::Grouprubyist

Going My Ruby Way このページをアンテナに追加 RSSフィード

Ruby ロゴ (C) Ruby Association LLC

2011年07月24日(日)

cond もどき

| 22:49 |  cond もどき - Going My Ruby Way を含むブックマーク はてなブックマーク -  cond もどき - Going My Ruby Way  cond もどき - Going My Ruby Way のブックマークコメント

LISP の cond みたいなことをしたかったので、ちょっと書いてみました。

実行するか否か判定する proc と実際の処理の proc を組にして持つ。

(下の例では is_even が真の場合 mul10 を実行)

proc は 1引数。条件に合う1組のみ実行。後の詳しいことはスクリプト参照してください。

cond.rb

#!/usr/bin/env ruby
# -*- coding: utf-8 -*-

require 'object'

class Cond
  def initialize(selectors=nil, &block)
    @selectors = selectors

    cascade(&block)
  end

  attr_reader :selectors

  def add(selectors={})
    @selectors.nil?           ? @selectors = selectors      :
    @selectors.kind_of?(Hash) ? @selectors.merge(selectors) :
                                @selectors.unshift(*selectors)
    self
  end

  alias << add

  def to_proc
    lambda do |a|
      f = @selectors.find {|s,| s[a]}
      f ? f.last.call(a) : a
    end
  end
end

if __FILE__ == $0
  require 'test/unit'

  class TestCond < Test::Unit::TestCase
    def setup
      is_even = lambda {|n| n.even? }
      is_odd  = lambda {|n| n.odd?  }
      mul10   = lambda {|n| n * 10  }
      sub5    = lambda {|n| n - 5   }

      @cond = Cond.new( is_even => mul10, is_odd => sub5 )
     
    # @cond = Cond.new([[is_even, mul10], [is_odd, sub5]]) でも同じ
    end
    def test_cond
      assert_equal expected = [-4, 20, -2, 40, 0, 60, 2, 80, 4, 100],
                   actual   = (1..10).map(&@cond)

    end
  end
end

# vi:set ts=2 sw=2 et fenc=UTF-8:

...なんかイマイチ。

----

FizzBuzz をやってみました。(irb -r cond)

>> c = Cond.new(
?> -> n { n % 15 == 0 } => -> x { 'FizzBuzz' },
?> -> n { n %  3 == 0 } => -> x { 'Fizz' },
?> -> n { n %  5 == 0 } => -> x { 'Buzz' })
=> #<Cond:0x00000001f141f8 @selectors= ....
......
>> (1..100).map(&c).each {|x| p x }
1
2
"Fizz"
4
"Buzz"
"Fizz"
7
8
"Fizz"
"Buzz"
11
"Fizz"
13
14
"FizzBuzz"
16
:
>>

これは、何となくはまってる気がする。