In Ruby, why is a method invocation not able to be treated as a unit when “do” and “end” is used?

后端 未结 2 547
滥情空心
滥情空心 2020-12-20 15:21

The following question is related to the question \"Ruby Print Inject Do Syntax\". My question is, can we insist on using do and end and make it wo

相关标签:
2条回答
  • 2020-12-20 15:42

    The issue here isn't just your parentheses: it's primarily the space after puts before the parentheses.

    With the code

    a = [1,2,3,4]
    
    puts (a.inject do |sum, x|
                 sum + x
                        end)
    

    We get the syntax errors you listed in the question.

    If you drop the space after puts,

    a = [1,2,3,4]
    
    puts(a.inject do |sum, x|
                 sum + x
                        end)
    

    prints out 10 as expected.

    Finally, using puts ((a.inject... with the space and double parentheses also prints out 10, but running that through ruby -cw XXX.rb tells us:

    a.rb:5: warning: (...) interpreted as grouped expression

    Syntax OK

    ruby -cw is used to Check the syntax with full Warnings turned on. When -cw is on, you will be warned about dubious parentheses and grouping. The error I'm more used to seeing is "don't put space before argument parentheses" -- so don't do that either!

    Lastly, the reason a.inject do fails without parentheses but a.inject { works, is that braces have a higher precedence than do/end. As a very rough guideline, you could say that p a.map { foo } is equivalent to p(a.map do foo end); and p a.map do foo end is equivalent to (p a.map) do foo end, which of course does not take a block argument.

    See also the Ruby quick reference on blocks (particularly the last two lines):

    Blocks, Closures, and Procs

    Blocks/Closures

    • blocks must follow a method invocation:

    invocation do ... end

    invocation { ... }

    • Blocks remember their variable context, and are full closures.
    • Blocks are invoked via yield and may be passed arguments.
    • Brace form has higher precedence and will bind to the last parameter if invocation made w/o parens.
    • do/end form has lower precedence and will bind to the invocation even without parens.
    0 讨论(0)
  • 2020-12-20 15:58

    From the (unofficial) ruby grammar, we see that the contents of (...) in puts (...) must be CALL_ARGS, which don't directly reduce to STMT. However, they can reduce to '(' COMPSTMT ')'. By including an extra set of parentheses, you can use do ... end.

    a = [1,2,3,4]
    
    puts ((a.inject do |sum, x| 
             sum + x   
           end))
    
    0 讨论(0)
提交回复
热议问题