为什么Ruby在抛出NameError异常后会保留代码评估?

And*_*eko 6 ruby

我无法向自己解释的简单代码:

puts a if a = 1
Run Code Online (Sandbox Code Playgroud)

这导致了

warning: found = in conditional, should be ==
NameError: undefined local variable or method 'a' for main:Object
Run Code Online (Sandbox Code Playgroud)

虽然现在经过检查a我们可以看到它已被定义:

a #=> 1
Run Code Online (Sandbox Code Playgroud)

尽管抛出了异常,为什么a还要分配1

来自文档:

混淆来自表达式的无序执行.首先分配局部变量,然后尝试调用不存在的方法[ a].

这部分仍然令人困惑 - 为什么解释器没有检测到已定义的局部变量a,仍然试图调用"不存在"的方法?如果它也不检查局部变量,找到定义的局部变量a并打印1

Ste*_*fan 5

让我们来看看Ruby的修饰符的抽象语法树if:

$ ruby --dump=parsetree -e 'puts a if a = 1'

# @ NODE_SCOPE (line: 1, code_range: (1,0)-(1,15))
# +- nd_tbl: :a
# +- nd_args:
# |   (null node)
# +- nd_body:
#     @ NODE_PRELUDE (line: 1, code_range: (1,0)-(1,15))
#     +- nd_head:
#     |   (null node)
#     +- nd_body:
#     |   @ NODE_IF (line: 1, code_range: (1,0)-(1,15))
#     |   +- nd_cond:
#     |   |   @ NODE_DASGN_CURR (line: 1, code_range: (1,10)-(1,15))
#     |   |   +- nd_vid: :a
#     |   |   +- nd_value:
#     |   |       @ NODE_LIT (line: 1, code_range: (1,14)-(1,15))
#     |   |       +- nd_lit: 1
#     |   +- nd_body:
#     |   |   @ NODE_FCALL (line: 1, code_range: (1,0)-(1,6))
#     |   |   +- nd_mid: :puts
#     |   |   +- nd_args:
#     |   |       @ NODE_ARRAY (line: 1, code_range: (1,5)-(1,6))
#     |   |       +- nd_alen: 1
#     |   |       +- nd_head:
#     |   |       |   @ NODE_VCALL (line: 1, code_range: (1,5)-(1,6))
#     |   |       |   +- nd_mid: :a
#     |   |       +- nd_next:
#     |   |           (null node)
#     |   +- nd_else:
#     |       (null node)
#     +- nd_compile_option:
#         +- coverage_enabled: false
Run Code Online (Sandbox Code Playgroud)

对于标准if:

$ ruby --dump=parsetree -e 'if a = 1 then puts a end'

# @ NODE_SCOPE (line: 1, code_range: (1,0)-(1,24))
# +- nd_tbl: :a
# +- nd_args:
# |   (null node)
# +- nd_body:
#     @ NODE_PRELUDE (line: 1, code_range: (1,0)-(1,24))
#     +- nd_head:
#     |   (null node)
#     +- nd_body:
#     |   @ NODE_IF (line: 1, code_range: (1,0)-(1,24))
#     |   +- nd_cond:
#     |   |   @ NODE_DASGN_CURR (line: 1, code_range: (1,3)-(1,8))
#     |   |   +- nd_vid: :a
#     |   |   +- nd_value:
#     |   |       @ NODE_LIT (line: 1, code_range: (1,7)-(1,8))
#     |   |       +- nd_lit: 1
#     |   +- nd_body:
#     |   |   @ NODE_FCALL (line: 1, code_range: (1,14)-(1,20))
#     |   |   +- nd_mid: :puts
#     |   |   +- nd_args:
#     |   |       @ NODE_ARRAY (line: 1, code_range: (1,19)-(1,20))
#     |   |       +- nd_alen: 1
#     |   |       +- nd_head:
#     |   |       |   @ NODE_DVAR (line: 1, code_range: (1,19)-(1,20))
#     |   |       |   +- nd_vid: :a
#     |   |       +- nd_next:
#     |   |           (null node)
#     |   +- nd_else:
#     |       (null node)
#     +- nd_compile_option:
#         +- coverage_enabled: false
Run Code Online (Sandbox Code Playgroud)

唯一的区别是方法参数puts:

#     |   |       |   @ NODE_VCALL (line: 1, code_range: (1,5)-(1,6))
#     |   |       |   +- nd_mid: :a
Run Code Online (Sandbox Code Playgroud)

VS:

#     |   |       |   @ NODE_DVAR (line: 1, code_range: (1,19)-(1,20))
#     |   |       |   +- nd_vid: :a
Run Code Online (Sandbox Code Playgroud)

使用修饰符if,解析器将其a视为方法调用并创建一个NODE_VCALL.这指示解释器来进行方法调用(虽然有一个局部变量a),产生了NameError.(因为没有方法a)

使用标准if,解析器将其a视为局部变量并创建一个NODE_DVAR.这指示解释器查找按预期工作的局部变量.

如您所见,Ruby在解析器级别识别局部变量.这就是文档说的原因:(强调添加)

由于解析顺序,修饰符和标准版本不是彼此的精确转换.