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 }")
3
  • 1
    If I understood your question, it is not possible: stackoverflow.com/questions/18552891/… Commented Nov 14, 2015 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. Commented Nov 14, 2015 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. Commented Nov 16, 2015 at 21:39

2 Answers 2

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
Sign up to request clarification or add additional context in comments.

8 Comments

This is not what I had in mind. I know about *args/**kwargs. I actually need the body to contain foo/bar variables.
so you want body to refer to local foo and bar variables?
@ndn please look at provided different solution
no. I need the body to actually reference arguments called foo and bar. Imagine the body can not be changed.
provides a solution (even though indirect) to the original problem. Appreciate the effort you put into this.
|
0

I assume you want to have dynamic arguments

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

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

1 Comment

This is not what I had in mind. I know about *args/**kwargs. I actually need the body to contain foo/bar variables.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.