Different behaviour of 'do .. end' and {..} block in ruby
sawa's answer is correct, but as the OP has asked for more clarification, I'm supplying my own answer.
All four of these method calls behave the same, passing a block to a foo
method:
foo { ... }
foo do ... end
foo() { ... }
foo() do ... end
When you write two methods and a block, without parentheses around the arguments, it is unclear which method the block goes with:
foo bar { ... }
foo bar do ... end
The question is: "Am I passing a block to bar
, and then passing its return value to foo
? Or am I calling foo
with bar
as an argument and also passing along a block?"
With parentheses, you can make this clear using either block style:
# Passing a block to `bar`, and then passing the result to `foo`
foo( bar { ... } )
foo( bar do ... end )
# Passing an argument and block to `foo`
foo( bar ) { ... }
foo( bar ) do ... end
The difference between {…}
and do…end
that you have run into is where Ruby chooses to put the parentheses when you omit them. The two block notations have different precedence, and so you end with different results:
# Passing a block to `bar`, and then passing the result to `foo`
foo bar{ ... }
foo( bar do ... end )
# Passing an argument and block to `foo`
foo bar do ... end
foo( bar ){ ... }
So, specifically in your case:
# This code…
p a.sort do |x,y|
y <=> x
end
# …is the same as this code:
b = a.sort
p(b){ |x,y| y <=> x }
# Note that you are passing a block to the `p` method
# but it doesn't do anything with it. Thus, it is
# functionally equivalent to just:
p a.sort
And,
# This code…
p a.sort { |x,y|
y <=> x
}
# …is functionally the same as this code:
b = a.sort{ |x,y| y <=> x }
p b
Finally, if you still don't get it, perhaps deeply considering the following code and output will help:
def foo(a=nil)
yield :foo if block_given?
end
def bar
yield :bar if block_given?
:bar_result
end
foo bar { |m| puts "block sent to #{m}" }
#=> block sent to bar
#=> foo got :bar_result
foo( bar do |m| puts "block sent to #{m}" end )
#=> block sent to bar
#=> foo got :bar_result
foo( bar ){ |m| puts "block sent to #{m}" }
#=> foo got :bar_result
#=> block sent to foo
foo bar do |m| puts "block sent to #{m}" end
#=> foo got :bar_result
#=> block sent to foo
Notice that the first and last examples in the code immediately above differ only in whether they use {…}
or do…end
for the block.
What is the difference or value of these block coding styles in Ruby?
The rails team and many other rubyists prefer to use curly braces for one line blocks and do...end
for multi-line ones.
The only functional difference between the two is that the precedence of a do...end
block is lower than that of a {...}
block.
Why does Ruby evaluate these two expressions differently? The only difference is {...} and do...end
Precedence matters. {}
has a higher precedence over method call. do
-end
has a lower precedence.
pp [1, 2, 3].map { |r| r + 1 }
is parsed as pp ([1, 2, 3].map { |r| r + 1 })
, which is:
pp([1, 2, 3].map { |r| r + 1 })
pp [1, 2, 3].map do |r| r + 1 end
is parsed as (pp [1, 2, 3].map) do |r| r + 1 end
, which is:
pp([1, 2, 3].map, &->(r){ r + 1 })
In the latter case passing block to pp
is a NOOP
.
How to write a switch statement in Ruby
Ruby uses the case
expression instead.
case x
when 1..5
"It's between 1 and 5"
when 6
"It's 6"
when "foo", "bar"
"It's either foo or bar"
when String
"You passed a string"
else
"You gave me #{x} -- I have no idea what to do with that."
end
Ruby compares the object in the when
clause with the object in the case
clause using the ===
operator. For example, 1..5 === x
, and not x === 1..5
.
This allows for sophisticated when
clauses as seen above. Ranges, classes and all sorts of things can be tested for rather than just equality.
Unlike switch
statements in many other languages, Ruby’s case
does not have fall-through, so there is no need to end each when
with a break
. You can also specify multiple matches in a single when
clause like when "foo", "bar"
.
Ruby Array#product method behaviour when given a block
By "yield all combinations" it means that it will yield (supply) all combinations of elements in the target (self) and other (argument) arrays to the given block.
For example:
a = [1, 2]
b = [:foo, :bar]
a.product(b) { |x| puts x.inspect } # => [1, 2]
# [1, :foo]
# [1, :bar]
# [2, :foo]
# [2, :bar]
It is roughly equivalent to this function:
class Array
def yield_products(other)
self.each do |x|
other.each do |y|
yield [x, y] if block_given?
end
end
end
end
Related Topics
How to Reset Boolean to "Default: False" at End of Day
How to Create Two Routes in One Block in Grape
Rails Adding Multiple Objects to an Empty Array
How to Specify Formatting Options for To_Yaml in Ruby
How to Access Text Field in an Iframe
Should Repeat a Number of Times
Run Ruby Script That Is Stored on Internet
Generating Devise Controllers - Rails Devise
Ruby 2.1 with Erubis Template Engine
Adding Fields to Devise Sign Up Using Rails 4
Bundler How to Uninstall Conflicting Dependency
How to Get the File Creation Time in Ruby on Windows
-Bash: /Usr/Local/Bin/Heroku: /Usr/Local/Bin/Ruby: Bad Interpreter: No Such File or Directory
Ruby: Length of a Line of a File in Bytes
Install Gem Gives "Failed to Build Gem Native Extension."
How to Create Automatically a Instance of Every Class in a Directory