Определение переменной Ruby
Я наткнулся на странное поведение в ruby относительно определения переменных (и по дороге потерял коробку пончиков):
irb(main):001:0> if false
irb(main):002:1> a = 1
irb(main):003:1> end
=> nil
irb(main):005:0> a.nil?
=> true
irb(main):006:0> b.nil?
NameError: undefined local variable or method `b' for main:Object
from (irb):6
from /Users/jlh/.rbenv/versions/2.1.5/bin/irb:11:in `<main>'
Почему нет a.nil?
бросание undefined local variable
? Взгляните, например, на python (просто хотел сравнить его с интерпретируемым языком):
>>> if False:
... a = 1
...
>>> print a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
На скомпилированном языке это даже не скомпилируется.
- Означает ли это, что ruby хранит ссылку на эту переменную, даже если она не прошла этот фрагмент кода?
- Если да, то насколько глубокие ifs / else рассматриваются для определения переменной?
Я действительно не могу поверить, что это ожидаемое поведение в рубине. И это не зависит от irb, запуск его в блоке кода ruby / rails дает тот же результат.
2 ответа
В Ruby существует неоднозначность между ссылками на локальную переменную и сообщением, отправляемым неявному получателю без списка аргументов. Это означает
foo
может означать "разыменование локальной переменной" или "отправить сообщение" foo
в self
без аргументов ", то есть он может быть эквивалентен
binding.local_variable_get(:foo)
или же
self.foo()
# or
public_send(:foo)
Эта неоднозначность разрешается при разборе времени. Когда синтаксический анализатор встречает назначение foo
с этого момента будет foo
как локальная переменная, независимо от того, действительно ли выполняется выполнение. (Это то, что парсер не может определить статически, в конце концов. Просто подумайте о if rand > 0.5 then foo = 42 end
.)
На скомпилированном языке это даже не скомпилируется.
Нет такой вещи как скомпилированный язык. Компиляция и интерпретация - это черты компилятора или интерпретатора (дух!), А не языка. Языки не компилируются и не интерпретируются. Они просто есть.
Любой язык может быть реализован с помощью компилятора, и каждый язык может быть реализован с помощью интерпретатора. Большинство языков имеют как скомпилированные, так и интерпретируемые реализации (например, C имеет GCC и Clang, которые являются компиляторами, и Cint и Cling, которые являются интерпретаторами, Haskell имеет GHC, который является компилятором, и Hugs, который является интерпретатором).
Многие современные языковые реализации имеют обе в одной и той же реализации, либо на разных этапах (например, YARV и MRuby компилируют исходный код Ruby во внутренний байт-код, а затем интерпретируют этот байт-код), либо в движке смешанного режима (например, JVM HotSpot интерпретирует и компилирует Байт-код JVM, в зависимости от того, что имеет больше смысла), или оба (например, Rubinius на первом этапе компилирует исходный код Ruby в байт-код Rubinius, а затем оба компилируют этот байт-код в собственный код и интерпретируют его, в зависимости от того, что имеет больше смысла).
Фактически, все существующие в настоящее время реализации Ruby компилируются: YARV и MRuby компилируются в свои собственные внутренние форматы байт-кода, Rubinius, MacRuby, MagLev и Topaz компилируются в свои собственные внутренние форматы байт-кода, а затем компилируются в собственный код, JRuby компилируется в байт-код JVM (которую JVM может или не может компилировать дальше), IronRuby компилируется в байт-код CIL (который VES может или не может компилировать дальше).
Тот факт, что Ruby ведет себя таким образом, объясняется тем, что в спецификации языка так сказано. Не потому, что Ruby "интерпретируется", потому что на самом деле это не так. Единственной чисто интерпретируемой реализацией Ruby были MRI и очень ранние версии JRuby, и обе давно устарели.
Я могу ошибаться, но Ruby определяет области для ваших переменных. У вас есть глобальная область действия, которая составляет $
Затем у вас есть локальная область действия вашего запущенного скрипта, который вы продемонстрировали в вопросе. Вы можете определить переменную внутри метода, но она все равно будет доступна в локальной области запущенного скрипта.
источник: http://ruby-doc.org//docs/ruby-doc-bundle/UsersGuide/rg/localvars.html
Здесь показано, что локальные переменные не имеют начального значения nil, но после определения принимают любое значение, независимо от того, были ли они определены.
2.1.5 :001 > p 1
1
=> 1
2.1.5 :002 > p a
NameError: undefined local variable or method `a' for main:Object
from (irb):2
from /Users/deh0002a/.rvm/rubies/ruby-2.1.5/bin/irb:11:in `<main>'
2.1.5 :003 > if false
2.1.5 :004?> a = 2
2.1.5 :005?> else
2.1.5 :006 > a = 3
2.1.5 :007?> end
=> 3
2.1.5 :008 > p a
3
=> 3
2.1.5 :009 > p$a
nil
=> nil
2.1.5 :010 > p @a
nil
=> nil
2.1.5 :011 >
Разница опять же в переменных Global и Instance. Даже если они не были определены, они автоматически принимают значение nil.