什么时候解析一行Ruby?评价的?执行?

什么时候解析一行Ruby?评价的?执行?,ruby,abstract-syntax-tree,internals,Ruby,Abstract Syntax Tree,Internals,我有点惊讶地发现,person由以下代码行定义,即使params[:person\u id]不存在: person = Person.find(params[:person_id]) if params[:person_id] 我有点希望Ruby首先检查if语句,然后只定义person。实际上,person的定义似乎早于此,但仍然是nil 在调查过程中,我尝试了以下方法: irb> foo # NameError (undefined local variable or method `

我有点惊讶地发现,
person
由以下代码行定义,即使
params[:person\u id]
不存在:

person = Person.find(params[:person_id]) if params[:person_id]
我有点希望Ruby首先检查
if
语句,然后只定义
person
。实际上,
person
的定义似乎早于此,但仍然是
nil

在调查过程中,我尝试了以下方法:

irb> foo
# NameError (undefined local variable or method `foo' for main:Object)
irb> if false
irb>   foo = 'bar'
irb> end
irb> foo
# => nil
最初,
foo
未定义。但是它会被定义,即使它只在未计算的
if
块中被引用

我现在猜测,整个程序被解析(?),并且一个
foo
节点被添加到抽象语法树(即定义的)中。然后执行程序(?),但跳过该特定行(未计算(?),因此foo为
nil
(已定义但未设置为值)


我不知道如何证实或反驳这种预感。如何学习和挖掘Ruby内部,并找出在这个特定场景中会发生什么?

回答我自己的问题,链接到一个

局部变量是在解析器遇到赋值时创建的,而不是在赋值发生时创建的

对此有更深入的分析(没有可用的部分链接,请搜索或滚动到“局部变量定义”部分):

顺便说一句,它是在“它出现”时定义的,这意味着即使它没有被分配,也会被定义。已定义[但尚未分配]变量的初始值为零

这回答了最初的问题,但不是如何了解更多


杰伊和两人都是帕特·肖内西建议的,我很想读这本书

此外,Ruby黑客指南的其余部分涵盖了很多细节,并实际检查了底层C代码。和章节与最初关于变量赋值的问题特别相关(与本章内容无关,它只是让您回到对象章节)

我还发现,查看解析器工作原理的有用工具是。一旦安装了(
gem install parser
),您就可以开始检查不同的代码位,以查看解析器对它们做了什么

这个gem还绑定了
ruby parse
实用程序,它允许您检查ruby解析不同代码片段的方式。
-E
-L
选项是我们最感兴趣的,如果我们只想处理Ruby片段,比如
foo='bar'
,那么
-E
选项是必需的。例如:

> ruby-parse -E -e "foo = 'bar'"
foo = 'bar'   
^~~ tIDENTIFIER "foo"                           expr_cmdarg  [0 <= cond] [0 <= cmdarg] 
foo = 'bar'   
    ^ tEQL "="                                  expr_beg     [0 <= cond] [0 <= cmdarg] 
foo = 'bar'   
      ^~~~~ tSTRING "bar"                       expr_end     [0 <= cond] [0 <= cmdarg] 
foo = 'bar'   
           ^ false "$eof"                       expr_end     [0 <= cond] [0 <= cmdarg] 
(lvasgn :foo
  (str "bar"))

链接到顶部的两个参照都高亮显示了一个边案例。如果a=0.zero,则使用示例
p a;
whlie如果lvar=true,则使用等效示例
p(lvar),这两个示例都会引发
名称错误

旁注:记住
=
表示分配,
=
表示比较。edge案例中的
if foo=true
构造告诉Ruby检查表达式
foo=true
的计算结果是否为true。换句话说,它将值
true
分配给
foo
,然后检查该分配的结果是否为
true
(将是)。这很容易与更常见的
if foo==true
混淆,后者只是检查
foo
true
的比较是否相等。因为这两者很容易混淆,所以如果我们在条件中使用赋值运算符,Ruby将发出警告:
警告:在条件中找到“=literal”,应该是==

使用
ruby parse
实用程序,让我们将原始示例
foo='bar'if false
,与边缘情况
foo if foo=true

> ruby-parse -L -e "foo = 'bar' if false"
s(:if,
  s(:false),
  s(:lvasgn, :foo,
    s(:str, "bar")), nil)
foo = 'bar' if false
            ~~ keyword         
~~~~~~~~~~~~~~~~~~~~ expression
s(:false)
foo = 'bar' if false
               ~~~~~ expression
s(:lvasgn, :foo,
  s(:str, "bar"))
foo = 'bar' if false     # Line 13
~~~ name                 # <-- `foo` is a name
    ~ operator        
~~~~~~~~~~~ expression
s(:str, "bar")
foo = 'bar' if false
          ~ end
      ~ begin         
      ~~~~~ expression
ruby parse-L-e“foo='bar'如果为false” s(:如果, s(:假), s(:lvasgn,:foo, s(:str,“bar”)、nil) foo='bar'如果为false ~~关键字 ~~~~~~~~~~~~~~~~~~~~~~~~~表情 s(:假) foo='bar'如果为false ~~~~~~表情 s(:lvasgn,:foo, s(:str,“bar”)) foo='bar'如果为false#第13行 ~~~name#ruby parse-L-e“foo if foo=true” s(:如果, s(:lvasgn,:foo, s(:true)), s(:send,nil,:foo),nil) 如果foo=true,则为foo ~~关键字 ~~~~~~~~~~~~~~~~~~~~~表情 s(:lvasgn,:foo, s(:真) 如果foo=true#第10行
~~~~名字#这是我给a的一个答案。你可以试试我从这个答案链接的文档。我听说《显微镜下的Ruby》也是一本好书(我自己没读过),我的理解是这是一种边缘案例。。。不幸的是,我认为这个问题太宽泛了,但也许有人会写出一个全面的答案。《显微镜下的Ruby》是一本很好的书,它将回答这个问题以及更多问题。非常有趣的阅读。很棒的自我回答。
> ruby-parse -L -e "foo = 'bar' if false"
s(:if,
  s(:false),
  s(:lvasgn, :foo,
    s(:str, "bar")), nil)
foo = 'bar' if false
            ~~ keyword         
~~~~~~~~~~~~~~~~~~~~ expression
s(:false)
foo = 'bar' if false
               ~~~~~ expression
s(:lvasgn, :foo,
  s(:str, "bar"))
foo = 'bar' if false     # Line 13
~~~ name                 # <-- `foo` is a name
    ~ operator        
~~~~~~~~~~~ expression
s(:str, "bar")
foo = 'bar' if false
          ~ end
      ~ begin         
      ~~~~~ expression
> ruby-parse -L -e "foo if foo = true"
s(:if,
  s(:lvasgn, :foo,
    s(:true)),
  s(:send, nil, :foo), nil)
foo if foo = true
    ~~ keyword              
~~~~~~~~~~~~~~~~~ expression
s(:lvasgn, :foo,
  s(:true))
foo if foo = true         # Line 10
       ~~~ name           # <-- `foo` is a name
           ~ operator       
       ~~~~~~~~~~ expression
s(:true)
foo if foo = true
             ~~~~ expression
s(:send, nil, :foo)
foo if foo = true         # Line 18
~~~ selector              # <-- `foo` is a selector
~~~ expression