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
Why Is "Slurping" a File Not a Good Practice
How to Avoid Running Activerecord Callbacks
No Increment Operator (++) in Ruby
Continuously Read from Stdout of External Process in Ruby
How to Validate a Date in Rails
Why Are Gems Installed in a Directory With a Different Ruby Version Than I'M Running
When Should I Use Struct Vs. Openstruct
Accessing Elements of Nested Hashes in Ruby
How to Test If a String Is Basically an Integer in Quotes Using Ruby
How to Get the Name of the Calling Method
Why Does Ruby Have Both Private and Protected Methods