Different Behaviour of 'Do .. End' and "{..}" Block in Ruby

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



Leave a reply



Submit