2007年 11月 19日

Ruby, Proc#curry

引数全部カリー化したい (一回だけじゃなくて)。可変長引数は考慮しない。

Lambda = Proc
class Lambda
	def curry
		s = self
		args = []
		(1...self.arity).inject(lambda {|x| p args; s[*(args+[x])] }) {|r,i|
			lambda {|x|
				args = args + [x]
				r
			}
		}
	end
end

sum = lambda {|x, y, z| x - y + z }.curry
p sum[1][2][3] # 2
p sum[1][2][3] # args が破壊されて失敗

なんかいろいろやってみたけどうまくいかないので eval に逃げ

Lambda = Proc
class Lambda
	def curry
		s = <<-EOS.gsub(/^¥t{3}/, "")
			lambda {|al|
				args = [#{(1...self.arity).inject(""){|r,i|r<<"a#{i}, "}}al]
				ObjectSpace._id2ref(#{self.object_id})[*args]
			}
		EOS
		eval (1...self.arity).inject(s) {|r,i|
			<<-EOS.gsub(/^¥t{4}/, "")
				lambda {|a#{self.arity-1-i+1}|
					#{r}
				}
			EOS
		}
	end
end

sum = lambda {|x, y, z| x - y + z }.curry
p sum[1][2][3] # 2
p (sum1 = sum[1])
p sum1[2][3]
p lambda {|x, y, z, a| x + y + z + a}.curry[1][2][3][4]


これ GC で死ぬよね。参照保持しないと

もっと簡単に書けた。あと ObjectSpace._id2ref は $SAFE 高いとつかえない。

class Lambda
	def curry
		s = <<-EOS.gsub(/^\t{3}/, "")
			lambda {|al|
				args = [#{(1...self.arity).inject(""){|r,i|r<<"a#{i}, "}}al]
				self[*args]
			}
		EOS
		instance_eval (1...self.arity).inject(s) {|r,i|
			<<-EOS.gsub(/^\t{4}/, "")
				lambda {|a#{self.arity-1-i+1}|
					#{r}
				}
			EOS
		}
	end
end