酒井です。

From: Shin-ichiro HARA <sinara / blade.nagaokaut.ac.jp>
Subject: [ruby-list:35092] Re: かりー化
Date: Mon, 13 May 2002 19:53:25 +0900

> 情報満載なので、いつも「日々の流転」楽しみにしております。_o_

うおぉ、そんな風に思われていたとは光栄です。

> 酒井さんの「メソッドのカリー化」のコードは載せないんですか?

では載せましょう。

ただ、こっちは
・ブロックの事を考慮してない
・UnboundMethodを使ってるので継承と相性が悪そうだ
といった理由で自分ではイマイチだと思っています。

require 'thread'

class Module
  CURRYING_METHOD_TABLE_       = Array.new
  CURRYING_METHOD_TABLE_MUTEX_ = Mutex.new

  def currying(name, arity=nil)
    m = instance_method(name)
    
    if m.arity == 0
      return
    elsif m.arity > 0
      arity = m.arity
    else
      arity ||= (-m.arity)-1
    end

    tbl_idx = nil
    CURRYING_METHOD_TABLE_MUTEX_.synchronize{
      tbl_idx = CURRYING_METHOD_TABLE_.size
      CURRYING_METHOD_TABLE_ << m
    }

    module_eval %Q[
      def #{name.to_s}(*args1)
        proc = lambda{|*args|
          if args.size >= #{arity}
            Module::CURRYING_METHOD_TABLE_[#{tbl_idx}].bind(self).call(*args)
          else
            lambda{|*a| proc.call(*(args + a))}
          end
        }
        proc.call(*args1)
      end
    ]
  end
end

class <<self
  define_method(:currying, method(:currying).to_proc)
end

if __FILE__ == $0
  def hello(a, out)
    out.printf("Hello, %s!\n", a)
  end
  currying :hello

  hello_ruby = hello("Ruby")
  hello_ruby[STDOUT] #=> Hello, Ruby!
end 

> そういえば「カリー化」の定義を言わないと。

すっかり忘れていました。(^^;;

僕は正確な定義は良く知らないのですが、原さんが下に書いているように
X×Y→Z のような多引数の関数を X→(Y→Z) というような
_関数を返り値とする_1引数の関数に変換する事をカリー化と呼ぶらしいです。

Rubyで使う場合には、引数の数を1個に制限するよりは
一度に可変個の引数を受け取れた方が嬉しいだろうという事で、
このようなコードにしてみました。

> 多変数の関数、例えば f(x, y, z) に、対して
> 
>     F(x)(y)(z) = f(x, y, z)
> 
> を満たすような、関数の関数の、、、関数 F を作る事をカリー化って
> いうんですよね。ここではその Proc バージョンであると。

-- さかい