0

What I basically want to achieve is:

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

Is there a way to do that other than building the entire lambda as string and evaling it?

eval("->(#{arguments.join(', ')}) { foo * bar }")
ndnenkov
  • 35,425
  • 9
  • 72
  • 104
  • 1
    If I understood your question, it is not possible: http://stackoverflow.com/questions/18552891/how-to-dynamically-create-a-local-variable – Thomas Nov 14 '15 at 12:36
  • As @Thomas suggests, to do this one needs to create local variables for the lambda's block variables, but local variables cannot be created in v1.9+; in v1.8 they can only be created by using `eval`. – Cary Swoveland Nov 14 '15 at 17:37
  • Ruby can be hard if you want to use it like C++. You cannot use variables' name from scope A and reuse them in scope B meanwhile replacing their referenced value. Take a look at Lambda Calculus and Beta reduction. – karatedog Nov 16 '15 at 21:39

2 Answers2

2

Like this:

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

or if you prefer lambda syntax:

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

After comments, maybe this is your solution:

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

After more comments two more tries: simpler:

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

And more complex one using proxy class to set context properly:

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)

And after lot of comments I believe this is the closest one to OP request:

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
Esse
  • 3,278
  • 2
  • 21
  • 25
0

I assume you want to have dynamic arguments

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

 multiplicator.call([4,5,6])
 => 120
sugaryourcoffee
  • 879
  • 8
  • 14
  • This is not what I had in mind. I know about `*args/**kwargs`. I actually need the body to contain `foo`/`bar` variables. – ndnenkov Nov 14 '15 at 12:33