Область видимости переменных и порядок разбора и операций: назначение в "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 локальная переменная или метод".