Confused about when to use parentheses or blocks in Ruby
On the surface, Ruby seems to be very similar to other objects like Java, Php, C, etc.
but things get a little weird when we start to run into blocks.
for example this works
(0...8).max()
=> 7
But it is not
(0...8).map(puts "hello world")
hello world
ArgumentError: wrong number of arguments(1 for 0)
In contrast, the map method accepts no arguments, but blocks, so passing the replacement ()
using to {}
fix the error.
(0...8).map{puts "hello world"}
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
=> [nil, nil, nil, nil, nil, nil, nil, nil]
And then there are some methods that must take both blocks and arguments
8.downto(1){puts "hello world"}
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
=> 8
I have a problem if I have to use ()
, {}
or both methods. On what basis is this decided?
- Is it fixed on a per method basis and I just remember what the method takes (blocks or parameters)?
- Or is there some other logical reasoning that determines if a method takes blocks
{}
or params()
?
Please help me understand
source to share
The parentheses after method calls are actually optional.
(0...8).map{puts "hello world"}
equivalent to (0...8).map() {puts "hello world"}
This way you don't really replace them.
The acceptance of blocks is entirely method dependent, they can be specified in the method declaration:
def method(param, &block)
block.call
end
which will allow you to access the block as a variable from the method and is called, for example, via block.call
.
They can also be used implicitly using the keyword yield
:
def method(param)
yield
end
Therefore, you need to refer to the API docs to be sure what is accepted or not.
source to share
It should be documented in the API of this method.
If there is a method that takes an argument, the block is usually optional - in which case it is used to "refine" the way the method works - for example Hash.new
, which can take an argument (the default hash value) and the block that is used for smart operations during initialization key values. But most of the methods that require a block do not take an argument, eg. Array#select
or Array#map
.
source to share
You use parentheses to pass arguments to a function. It is always a comma-separated list of objects. They can be the result of a function, but they don't have to. The function they result in will always be called only once.
Example:
x = Array.new
=> []
x.push(2)
=> [2]
x.push(7.modulo(4))
=> [2,3]
You use blocks to pass functional behavior to a method. Typically, these functions are designed to be called with many different arguments or a specific number of times. puts "hello world"
is a function that prints something, but only returns nil
. Thus, you will almost never try to use this as an argument to a function (as you would pass to nil
that function), but usually you would pass it as a function block so that it could be called multiple times.
Most of the time you will want to have a block that you pass to a function, use arguments so that it actually does something different for each call.
Example:
(0..8).each{ |number|
if number.odd?
puts "#{number} is odd"
end
}
=> 1 is odd
=> 3 is odd
=> 5 is odd
=> 7 is odd
=> => 0..8
source to share