Определение переменной 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.

Другие вопросы по тегам