我无法向自己解释的简单代码:
- puts a if a = 1
这导致了
- warning: found = in conditional,should be ==
- NameError: undefined local variable or method 'a' for main:Object
虽然,现在检查我们可以看到,它已被定义:
- a #=> 1
尽管抛出了异常,为什么将其分配给1?
从docs:
The confusion comes from the out-of-order execution of the expression.
First the local variable is assigned-to then you attempt to call a
nonexistent method [a
].
这部分仍然令人困惑 – 为什么解释器没有检测到已经定义的局部变量a并仍然试图调用“不存在”的方法?如果它也不检查局部变量,找到定义的局部变量a并打印1?
解决方法
让我们来看看Ruby的修饰符的抽象语法树,如果:
- $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,15))
- # +- nd_head:
- # | (null node)
- # +- nd_body:
- # | @ NODE_IF (line: 1,15))
- # | +- nd_cond:
- # | | @ NODE_DASGN_CURR (line: 1,10)-(1,15))
- # | | +- nd_vid: :a
- # | | +- nd_value:
- # | | @ NODE_LIT (line: 1,14)-(1,15))
- # | | +- nd_lit: 1
- # | +- nd_body:
- # | | @ NODE_FCALL (line: 1,6))
- # | | +- nd_mid: :puts
- # | | +- nd_args:
- # | | @ NODE_ARRAY (line: 1,5)-(1,6))
- # | | +- nd_alen: 1
- # | | +- nd_head:
- # | | | @ NODE_VCALL (line: 1,6))
- # | | | +- nd_mid: :a
- # | | +- nd_next:
- # | | (null node)
- # | +- nd_else:
- # | (null node)
- # +- nd_compile_option:
- # +- coverage_enabled: false
对于标准如果:
- $ruby --dump=parsetree -e 'if a = 1 then puts a end'
- # @ NODE_SCOPE (line: 1,24))
- # +- nd_tbl: :a
- # +- nd_args:
- # | (null node)
- # +- nd_body:
- # @ NODE_PRELUDE (line: 1,24))
- # +- nd_head:
- # | (null node)
- # +- nd_body:
- # | @ NODE_IF (line: 1,24))
- # | +- nd_cond:
- # | | @ NODE_DASGN_CURR (line: 1,3)-(1,8))
- # | | +- nd_vid: :a
- # | | +- nd_value:
- # | | @ NODE_LIT (line: 1,7)-(1,8))
- # | | +- nd_lit: 1
- # | +- nd_body:
- # | | @ NODE_FCALL (line: 1,20))
- # | | +- nd_mid: :puts
- # | | +- nd_args:
- # | | @ NODE_ARRAY (line: 1,19)-(1,20))
- # | | +- nd_alen: 1
- # | | +- nd_head:
- # | | | @ NODE_DVAR (line: 1,20))
- # | | | +- nd_vid: :a
- # | | +- nd_next:
- # | | (null node)
- # | +- nd_else:
- # | (null node)
- # +- nd_compile_option:
- # +- coverage_enabled: false
唯一的区别是puts的方法参数:
- # | | | @ NODE_VCALL (line: 1,6))
- # | | | +- nd_mid: :a
VS:
- # | | | @ NODE_DVAR (line: 1,20))
- # | | | +- nd_vid: :a
使用修饰符if,解析器将a视为方法调用并创建NODE_VCALL.这指示解释器进行方法调用(尽管存在局部变量a),从而产生NameError. (因为没有方法a)
使用标准if,解析器将a视为局部变量并创建NODE_DVAR.这指示解释器查找按预期工作的局部变量.
如您所见,Ruby在解析器级别识别局部变量.这就是文档说的原因:(强调添加)
the modifier and standard versions […] are not exact transformations of each other due to parse order.