Область видимости переменных и порядок разбора и операций: назначение в "if"
Я понимаю, что операторы if
в конце строки оцениваются перед кодом в начале строки:
'never shown' if (false)
И назначение возможно в инструкции if
.
'shown' if (value = 'dave is king')
value #=> "dave is king"
И, когда назначается переменная, которой не существует, она создается. Нет необходимости в том, чтобы он существовал заранее. Это правда?
Если все эти предположения верны, почему это не удается?
error_array << error if (error = import_value(value))
#=> undefined local variable or method `error' for
Ему присваивается ошибка до того, как массив нажат вправо? Я хочу понять, когда все оценивается.
Это работает:
if (error = import_value(value))
error_array << error
end
Теперь я действительно смущен.
Ответы
Ответ 1
Это происходит только при попытке присвоить буквальное значение, если вы вызываете функцию, с которой она работает.
def foo(a)
a
end
p 'not shown' if(value = foo(false))
p 'shown' if(value = foo(true))
# This outputs a Warning in IRB
p 'shown' if(value = false)
(irb):2: warning: found = in conditional, should be ==
Если вы включите отладку (-d), вы увидите предупреждение об используемой переменной value
warning: assigned but unused variable - value
Это "работает", потому что оператор действительно оценивает до true
насколько это возможно, что позволяет коду предшествовать его выполнению.
Что здесь происходит, так это то, что if() при использовании в качестве модификатора имеет собственную область привязки или контекст. Поэтому назначение никогда не встречается за пределами if, и поэтому не имеет смысла выполнять. Это отличается от структуры управления, потому что блок, который принимает оператор if, также находится в той же области, что и назначение, тогда как строка, предшествовавшая модификатору if, не входит в область if.
Другими словами, они не являются эквивалентными.
if a = some(value)
puts a
end
puts a if(a = some(value))
Первая имеет puts a
в пределах области if, последняя имеет puts a
вне области видимости и, следовательно, имеет разные привязки (что Ruby вызывает контекст).
Порядок действий Ruby
Ответ 2
В Ruby локальные переменные определяются синтаксическим анализатором, когда он впервые встречает назначение, и затем находятся в области действия с этой точки. Поскольку Ruby анализируется как английский, слева направо, сверху вниз, локальная переменная еще не существует, когда вы ее используете, потому что использование еще осталось от назначения.
Вот небольшая демонстрация:
foo # NameError: undefined local variable or method `foo' for main:Object
if false
foo = 42
end
foo # => nil
Как вы можете видеть, локальная переменная существует в строке 7, хотя назначение в строке 4 никогда не выполнялось. Он, однако, разбирался и почему локальная переменная foo
существует. Но поскольку назначение никогда не выполнялось, переменная не инициализируется и, следовательно, оценивается как nil
, а не 42
.
Теперь перейдем к простейшей версии вашего дела:
bar if bar = true
# warning: found = in conditional, should be ==
# NameError: undefined local variable or method `bar' for main:Object
bar # => true
Эта переменная создается при анализе присваивания, которое находится здесь:
bar if bar = true
^^^^^^^^^^
Но он используется здесь:
bar if bar = true
^^^
Это до назначения. Тот факт, что назначение выполняется до использования, не имеет значения, потому что синтаксический анализ здесь имеет значение, и присваивание получает синтаксический анализ после использования, что означает, что в момент использования парсер по-прежнему считает его вызовом метода без списка аргументов и неявным приемник (т.е. эквивалентен self.bar()
), а не локальная переменная.
Ответ 3
Я предполагаю, что порядок разбора отличается от (логического) порядка исполнения. В частности, данный
array << error if (error = some_function)
Тогда логически выполнение должно идти примерно как
- вызов
some_function
- Если
error
не определено, определите error
- присвойте возвращаемое значение
some_function
в error
- оцените
if
- Если
if
оценивается как true
, добавьте значение error
в array
Однако, разбор мудрых (при условии типичного парсера LR), он идет
- Получил токен
array
(идентификатор). Это определено? Да. Это переменная? Да.
- Получен токен
<<
(оператор). Отвечает ли array
на <<
? Да (в противном случае, вывод "<метод w90 > " ).
- Получил токен
error
(идентификатор). Это определено? Нет. Вывести "undefined локальная переменная или метод".