Published at: 2024-11-15

in Ruby, we can pass block argument to an function by prefixing with an &.

Symbol

a common example of the use of objects that implement to_proc in Symbol.

["a", "b", "c"].map { |s| s.upcase } # => ["A", "B", "C"]
["a", "b", "c"].map(&:upcase)        # => ["A", "B", "C"]

these two lines of code behave identically.

the reason this works is that Symbol implements the to_proc method. all it does was take the argument to this proc, and call the method whose name maches this symbol.

we’ll frequently see this syntax as a shortcut fot methods that take simple blocks like map or sort_by

Make it simpler

we can pass usual do blocks to function, but some times we can make it more simple. let’s see an example.

numbers = 1..5

operator = gets

number = Integer(gets)

if operator.start_with?("t")
  puts numbers.collect { |n| n * number }.join(", ")
else
  puts numbers.collect { |n| n + number }.join(", ")
end

this code works, but ugly. we can refactor it like this below.

numbers = 1..5

operator = gets

number = Integer(gets)

if operator.start_with?("t")
  calc = ->(n) { n * number }
else
  calc = ->(n) { n + number }
end

puts numbers.map(&calc).join(", ")

in this version, we assign the correct block to a variable named calc, then we pass calc to the method map, prefixing it with &

but there’s a even shorter way to write this code. Ruby object have a method name called method. which takes a symbol and returns the object’s method of the same name.

operators = get

number = Integer(gets)

method = number.method(operator.start_with?("t") ? :* : :+)

puts (1..5).map(&method).join(", ")

In this case, we’re using method to grab the method named :+ or :* based on the input and using the ampersand’s to_proc powers to create a proc that calls that method.