为什么`put(nil或4)`在Ruby中失败?

Nic*_* M. 18 ruby

当我做:

puts(nil or 4)
Run Code Online (Sandbox Code Playgroud)

Ruby抱怨:

SyntaxError: syntax error, unexpected keyword_or, expecting ')'
Run Code Online (Sandbox Code Playgroud)

这是为什么?puts(nil || 4)确实有效,但我想知道为什么or不这样做.我认为两者之间的区别只在于它们的运算符优先级.

(我知道这个表达nil or 4似乎没有用,因为它总是会返回4.这只是一个例子,为了简单起见.我的实际表达是Integer(ENV['WD'] or 4).)

Ser*_*sev 15

简短的回答

因为这是ruby语法的方式.

更长的答案

and/ orkeywords被设计用于控制流构造.考虑这个例子:

def die(msg)
  puts "Exited with: #{msg}"
end

def do_something_with(arg)
  puts arg
end

do_something_with 'foo' or die 'unknown error'
# >> foo
# >> Exited with: unknown error
Run Code Online (Sandbox Code Playgroud)

or由于ruby 解析规则(伪BNF),这里可以很好地使用ruby的可选括号.

简而言之,参数列表(CALL_ARGS)是ARG的列表,以逗号分隔.现在,大多数东西都是ARG(类定义,例如,通过作为PRIMARY),但不是简单的EXPR.如果用括号括起表达式,那么它将匹配"复合语句"的规则,因此将是一个PRIMARY,它是一个ARG.这意味着什么

puts( (nil or 4) ) # will work, compound statement as first argument
puts (nil or 4)  # same as above, omitted optional method call parentheses
puts(nil or 4) # will not work, EXPR can't be an argument
puts nil or 4 # will work as `puts(nil) or 4`
Run Code Online (Sandbox Code Playgroud)

您可以阅读上面引用的语法,以准确了解它的工作原理.

奖励:类定义是有效ARG的示例

puts class Foo
       def bar
         puts "hello"
       end
     end, 'second argument'

# >> bar # this is the "value" of the class definition
# >> second argument
Run Code Online (Sandbox Code Playgroud)

  • 很棒的解释! (3认同)
  • 知道了 “和”和“或”具有优先级,因为它们没有明确定义为ARG。如此之多的运营商被确定为ARG,因此很容易想到ARG和EXPR是相同的。讨厌。我认为如果没有`and`和`or`,情况会更好。 (2认同)
  • @DaveSchweisguth:是的,确切地说.我从不使用它们.:) (2认同)
  • @DaveSchweisguth和风格指南建议反对他们.https://github.com/bbatsov/ruby-style-guide#no-and-or-or (2认同)

saw*_*awa 10

这是因为or并且and具有比方法调用更低的优先级.您的表达式被解释为:

{puts(nil} or {4)}
Run Code Online (Sandbox Code Playgroud)

在哪里{}代表分组.语法错误来自表达式

puts(nil
Run Code Online (Sandbox Code Playgroud)

(以下也会引发语法错误):

4)
Run Code Online (Sandbox Code Playgroud)

如果通过在表达式周围放置一对括号来强制分组,那么它将按照您的预期方式工作:

puts((nil or 4))
Run Code Online (Sandbox Code Playgroud)

请注意,外部括号对用于方法调用,而不是分组,因此只有一对括号无法更改分组.

或者,如果您通过放置空格来消除用于分组的一对括号的歧义,那么这也将起作用:

puts (nil or 4)
Run Code Online (Sandbox Code Playgroud)


Nic*_* M. 7

@Sergio Tulentsev(和@sawa)给出了一个很好的答案,但我想改写它,以便我将来能够快速理解它:

Ruby允许我们在函数调用中删除括号.也就是说,而不是:

func1(ARG, ARG, ARG) or func2(ARG, ARG, ARG)
Run Code Online (Sandbox Code Playgroud)

我们可以做的:

func1 ARG, ARG, ARG or func2 ARG, ARG, ARG
Run Code Online (Sandbox Code Playgroud)

但是,为了使最后一行的行为类似于第一行,"或"不能是在ARG的顶层使用的运算符(否则最后一行将被解释为func1(ARG, ARG, ARG or func2 ARG, ARG, ARG)).实际上,当我们查看BNF时,我们看到ARG没有直接提及"或"/"和"(这意味着它在那里是非法的).

但ARG仍然可以使用"或":将表达式包装在括号中.在BNF中,我们将此视为ARG可以分支的主要替代方案(作为PRIMARY,反过来,分支到'(' COMPSTMT ')').

现在,关于为什么func (1 or 2)func((1 or 2))工作而func(1 or 2)不是:

  • func(1 or 2)是BNF所谓的FUNCTION,它扩展为OPERATION ['(' [CALL_ARGS] ')'],这意味着ARG是"1或2",但正如我们所见,ARG不能包含"或",所以它是无效的.

  • func((1 or 2))再次,OPERATION ['(' [CALL_ARGS] ')']但是这里ARG是"(1或2)",这是一个有效的 ARG(参见上面提到的PRIMARY).

  • func (1 or 2)是BNF调用的COMMAND,它扩展为OPERATION CALL_ARGS,这意味着ARG是"(1或2)",这是一个有效的 ARG(参见上面提到的PRIMARY).