什么时候解析一行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