Ruby动态定义参数

时间:2015-11-14 11:21:08

标签: ruby metaprogramming

我基本上想要实现的是:

arguments     = [:foo, :bar]
multiplicator = ->(_something_funky_with_arguments_) { foo * bar }
multiplicator.call(3, 4) # => 12

除了将整个lambda构建为字符串并eval之外,还有其他办法吗?

eval("->(#{arguments.join(', ')}) { foo * bar }")

2 个答案:

答案 0 :(得分:2)

像这样:

multiplicator = Proc.new {|*arguments| arguments.inject(&:*) }
multiplicator.call(3, 4) # => 12
multiplicator.call(3, 4, 5) # => 60

或者如果您更喜欢lambda语法:

multiplicator = ->(*arguments) { arguments.inject(&:*) }
multiplicator.call(3, 4) # => 12
multiplicator.call(3, 4, 5) # => 60

评论之后,也许这是你的解决方案:

foo = 3
bar = 4
arguments     = ["foo", "bar"]
multiplicator = ->(bind) { arguments.inject(1) { |acc, var| acc * eval(var, bind)} }
multiplicator.call(binding) # => 12

经过更多评论后,再尝试两次: 更简单的:

require 'ostruct'
structer = OpenStruct.new
structer.foo = 3
structer.bar = 4
multiplicator = ->() { foo * bar }
structer.define_singleton_method :call_me, &multiplicator
structer.call_me # => 12

更复杂的一个使用代理类来正确设置上下文:

class Proxy
  def set_lambda(lambda_object)
    define_singleton_method :run_me, &lambda_object
    return self
  end

  def call(arg_names, *args)
    arg_names.each_with_index do |var, i|
      define_singleton_method var do args[i] end
    end
    self.run_me
  end
end
multiplicator = ->() { foo * bar }
arguments     = [:foo, :bar]
Proxy.new.set_lambda(multiplicator).call(arguments, 3, 4)

经过大量评论后,我相信这是最接近OP请求的评论:

class Proxy
  def set_lambda(lambda_object)
    define_singleton_method :run_me, &lambda_object
    return self
  end

  def set_arguments(args)
    @args_table = args
    return self
  end

  def call(*args)
    @args_table.each_with_index do |var, i|
      define_singleton_method var do args[i] end
    end
    self.run_me
  end
end
multiplicator = ->() { foo * bar }
arguments     = [:foo, :bar]
callable = Proxy.new.set_lambda(multiplicator).set_arguments(arguments)
callable.call(3, 4) # => 12
callable.call(4, 5) # => 20

答案 1 :(得分:0)

我假设你想拥有动态参数

 multiplicator = ->(args) { args.inject(:*) }

 multiplicator.call([4,5,6])
 => 120