Ruby do/end vs braces

14,410

Solution 1

That's because the second line is interpreted as:

p(a.map) do ... end

instead of:

p(a.map do ... end)

The grammar is ambiguous in this case and do doesn't seem to bind as strongly as {.

Solution 2

That has to do with the difference in associativity of the { character and the do keyword.

In the first case, the block is interpreted as a block argument to the map function. The result of the map function is the argument to the p function.

In the second case, the block is interpreted as a block argument to the p function, while the a.map is interpreted as the first argument to the p function. Since a.map evaluates to a, this prints the original array. The block is effectively ignored in this case.

Solution 3

With the do/end syntax you are passing the block to p as a second argument, rather than to the map. You get the same result with:

p a.map

The block is ignored by p as it does not produce anything on inspect.

Share:
14,410

Related videos on Youtube

Grandpa
Author by

Grandpa

Ruby, Java and C hacker.

Updated on June 17, 2020

Comments

  • Grandpa
    Grandpa almost 4 years

    Why does this map expression produce different results depending on whether I use braces or do/end?

    a = [1,2,3,4,5]
    
    
    p a.map { |n|
        n*2
    }  
    #=> [2,4,6,8,10]
    
    
    p a.map do |n|
        n*2
    end  
    #=> [1,2,3,4,5]
    
    • fotanus
      fotanus almost 11 years
      This holdstrue up to today
    • Cees Timmerman
      Cees Timmerman over 3 years
      Does this answer your question? Using do block vs braces {}
  • sawa
    sawa about 13 years
    @Grandpa It's not particularly about 'p'. It's about the strength/priority between method's taking argument/blocks and {}, do end
  • karatedog
    karatedog almost 12 years
    And how is it possible to use do...end properly with a multiline block so that map gets it as parameter? Braces - as your suggestion - won't do it, Ruby throws an error, unexpected keyword_do_block even the whole block is one line.
  • DarkDust
    DarkDust almost 12 years
    @karatedog: You'll probably need to use an intermediate variable here as the parser doesn't support what you want.
  • karatedog
    karatedog almost 12 years
    @DarkDust Thanks, but I cannot use an intermediate variable when the Enumerator is infinite because I should have to know beforehand how much items I should generate into that intermediate variable.
  • DarkDust
    DarkDust almost 12 years
    I don't get what you mean, of course you can do foo = a.map do ... end ; p(foo). It is the exact same semantics, because map always needs to aggregate an array to pass on first. Whether that array is then stored in a variable or passed as parameter to another method makes no difference, the array needs to be created first no matter what.