Passing a lambda as a block

RubyLambda

Ruby Problem Overview


I'm trying to define a block that I'll use to pass the the each method of multiple ranges. Rather than redefining the block on each range, I'd like to create a lamba, and pass the lambda as such:

count = 0
procedure = lambda {|v| map[count+=1]=v}
("A".."K").each procedure
("M".."N").each procedure
("P".."Z").each procedure

However, I get the following error:

ArgumentError: wrong number of arguments(1 for 0)
from code.rb:23:in `each'
Any ideas what's going on here?

Ruby Solutions


Solution 1 - Ruby

Tack an ampersand (&) onto the argument, for example:

("A".."K").each &procedure

This signifies that you're passing it as the special block parameter of the method. Otherwise it's interpreted as a normal argument.

It also mirrors they way you'd capture and access the block parameter inside the method itself:

# the & here signifies that the special block parameter should be captured
# into the variable `procedure`
def some_func(foo, bar, &procedure)
  procedure.call(foo, bar)
end

some_func(2, 3) {|a, b| a * b }
=> 6

Solution 2 - Ruby

The trick is in using an & which tells Ruby to convert this argument to a Proc if necessary and then use the object as the method’s block. Starting from Ruby 1.9 there's a shortcut for lambda (anonymous) functions. So, you can write code like this:

(1..5).map &->(x){ x*x }
# => [1, 4, 9, 16, 25]

will take each element of the array and compute its power

it is the same as this code:

func = ->(x) { x*x }
(1..5).map &func

for Ruby 1.8:

(1..5).map &lambda {|x| x*x}
# => [1, 4, 9, 16, 25]

To solve your problem you can use Array's method reduce (0 is initial value):

('A'..'K').reduce(0) { |sum,elem| sum + elem.size }
# => 11

Passing a lambda function to reduce is a bit tricky, but the anonymous block is pretty much the same as lambda.

('A'..'K').reduce(0) { |sum, elem| ->(sum){ sum + 1}.call(sum) }
# => 11

Or you could concat letters just like this:

('A'..'K').reduce(:+)
=> "ABCDEFGHIJK"

Convert to lowercase:

('A'..'K').map &->(a){ a.downcase }
=> ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"]

In the context of a method definition, putting an ampersand in front of the last parameter indicates that a method may take a block and gives us a name to refer to this block within the method body.

Solution 3 - Ruby

The other answers left something un-clarified that I'd like to expand on. How do we pass an arg and a block to a method?

Suppose we have a method that takes an arg and a block:

def method_with_arg_and_block(arg)
  puts arg
  yield
end

and a proc:

pr = proc { puts 'This is a proc'}

The answer: It's important that you pass the proc in as an arg with an ampersand rather than appending the proc to the method (like you would do with a block).

For example if you do:

method_with_arg_and_block('my arg') &pr

you will get a "no block given (yield)" exception.

The correct way to call this is:

method_with_arg_and_block('my arg', &pr)

Ruby will take care of converting the proc to a block and appending it to your method. Note: since lambdas are also procs, this will work with lambdas as well.

Thanks to https://medium.com/@sihui/proc-code-block-conversion-and-ampersand-in-ruby-35cf524eef55 for helping me understand this.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionheneryvilleView Question on Stackoverflow
Solution 1 - Rubynumbers1311407View Answer on Stackoverflow
Solution 2 - RubyTombartView Answer on Stackoverflow
Solution 3 - RubydansterenView Answer on Stackoverflow