Ruby Block and Unparenthesized Arguments

Ruby block and unparenthesized arguments

It's because you're calling

pp x.map

and passing a block to pp (which ignores it)

As explained in the Programming Ruby book

Braces have a high precedence; do has a low precedence

So, effectively, braces tie to the function call closest to them (x.map) whereas do binds to the furthest away (pp). That's a bit simplistic but it should explain this situation

Different behavior with map, select, etc. when using do...end blocks

Operator precedence matters.

p h.select do |k, v| true end

is in fact executed as:

(p h.select) do |k, v| true end

while

p h.select { |k, v| true }

is treated as:

p (h.select { |k, v| true })

Enumerable#select, called without a block, returns an enumerator.

Passing block into a method - Ruby

The difference between do .. end and curly braces is that the curly braces bind to the rightmost expression, while do .. end bind to the leftmost one. Observe the following examples:

def first(x=nil)
puts " first(#{x.inspect}): #{block_given? ? "GOT BLOCK" : "no block"}"
"f"
end

def second(x=nil)
puts " second(#{x.inspect}): #{block_given? ? "GOT BLOCK" : "no block"}"
"s"
end

first second do |x| :ok end # second(nil): no block
# first("s"): GOT BLOCK

first second {|x| :ok } # second(nil): GOT BLOCK
# first("s"): no block

In the first case, the block made with do..end will be bound to the first function (leftmost). In the second case the block made with curly brackets will be bound to the second function (rightmost).

Usually it's good idea to use parentheses if you have two functions and a block - just for readability and to avoid mistakes.

It's very easy to accidentally pass a block to puts method, just as in your question.

differing `puts` behavior inside `do..end` blocks and curly braces in Ruby

do...end and {} are 100% semantically equivalent for method blocks. Their only difference is their parsing precedence, so they evaluate differently

To really understand that difference, a few things first.

Ruby lets you call methods without parens:

my_object.my_method my_arg

# so my_arg could actually be a method! Let's put parens in to show that:
my_object.my_method(my_arg())

Blocks in Ruby are method arguments -- a syntax for passing in closures (except for the special keywords that act in the parent scope). The below two chunks are equivalent:

[1, 2, 3].map { |x| 2 * x }

# split out into two lines
double = ->(x) { 2 * x } # shorthand for `lambda { |x| 2 * x }`
[1, 2, 3].map(&double)

Okay, so knowing all that, let's expose the difference between {} and do...end:

my_method [1, 2, 3].map { |x| 2 * x }

my_method([1, 2, 3].map { |x| 2 * x }) # parses like this


my_method [1, 2, 3].map do |x| 2 * x end

my_method([1, 2, 3].map) do |x| 2 * x end # parses like this

my_method([1, 2, 3].map) { |x| 2 * x } # in other words

{} has more precedence than do...end, immediately getting associated to the method immediately to its left. do...end has lower precedence, and will associate with my_method, which gets passed [1, 2, 3].map and the block as arguments.

This means, what you did above is:

puts(my_array.each) { |num| num *= 2; puts "The new number is #{num}." }

You've passed into puts the my_array.each, which is an enumerator, and a block, and puts does nothing with the blocks passed into it, as do all methods by default.

Weird imoperfection in Ruby blocks

It doesn't think it's a hash - it's a precedence issue. {} binds tighter than do end, so method :argument { other_method } is parsed as method(:argument {other_method}), which is not syntactically valid (but it would be if instead of a symbol the argument would be another method call).

If you add parentheses (method(:argument) { other_method }), it will work fine.

And no, the code is not actually valid. If it were, it would work.

in ruby, when is a block not a block?

Ruby thinks you are passing the block to pp method, which simply ignores it. Try:

res = remotes(forms,remotes) do |ident|
case(ident)
when :get_assets
'@Userobject'
end
end

pp res

Ruby Block Syntax Error

Try this:

version(:full)   { process(:resize_to_limit => [960, 960]) }
version(:half) { process(:resize_to_limit => [470, 470]) }
version(:third) { process(:resize_to_limit => [306, 306]) }
version(:fourth) { process(:resize_to_limit => [176, 176]) }

You have a precedence problem. The { } block binds tighter than a do...end block and tighter than a method call; the result is that Ruby thinks you're trying to supply a block as an argument to a symbol and says no.

You can see a clearer (?) or possibly more familar example by comparing the following:

[1, 2, 3].inject 0  { |x, y| x + y }
[1, 2, 3].inject(0) { |x, y| x + y }

Block not called in Ruby

Add parentheses to puts

puts(m do
x = 2
y = 3
x * y
end)

The output is 6.

Your code is equivalent to

puts(m) do 
x = 2
y = 3
x * y
end

unable to print output from my method, local jump error

Can someone explain where does the error come from

Lack of parentheses in your code. Your block binds to print, not bubble_sort_by.

and how can I fix it?

One way is to not print on the same line, but use a temp variable

sorted = bubble_sort_by(["hi","hello","hey"]) do |left,right|
left.length - right.length
end

print sorted

Another way is to use a curly-brace block syntax, it binds more strongly.

print bubble_sort_by(["hi","hello","hey"]) { |left,right| left.length - right.length }

The least preferred way (to me) is to parenthesize the sorting method, so that there's no doubt to which method the block belongs:

print(bubble_sort_by(["hi","hello","hey"]) do |left,right|
left.length - right.length
end)


Related Topics



Leave a reply



Submit