Глобальные и локальные рекурсивные функции в lua

Я очень новичок в lua, и я хотел бы понять следующее поведение.

Когда я пытаюсь запустить следующую функцию рекурсии:

local func = function ( n )
  if n == 1 then return 1
  else return n * func( n - 1 )
  end
end

print( func( 5 ) )

Программа завершится ошибкой:

lua: main.lua:16: attempt to call a nil value (global 'func')
stack traceback:
main.lua:16: in local 'func'
main.lua:38: in main chunk
[C]: in ?

что нормально, поскольку, согласно объяснению, локальная версия func< /em> переменная еще не известна, поэтому она пытается вызвать глобальную. Но когда я удаляю ключевое слово local, следующий код работает правильно?

func = function ( n )
  if n == 1 then return 1
  else return n * func( n - 1 )
  end
end

print( func( 5 ) )

В результате программа выводит 120, но глобальная функция func никогда раньше не инициализировалась и не использовалась. Как это возможно, чтобы это тоже не выдавало ошибку? Разве второй пример не ссылается на глобальную func, как в первом?


person esgaldir    schedule 11.01.2018    source источник


Ответы (2)


Разве второй пример не ссылается на глобальную функцию, как в первом?

It is :)

Видите ли, не имеет значения, существует значение или нет, когда вы компилируете новую функцию. Он не проверяет значение, хранящееся в переменной func, вместо этого он будет искать его каждый раз при вызове. Единственное, что важно, это видимость переменной. Если переменная не видна в локальной области видимости, она считается глобальной и будет искаться в глобальной таблице окружения. Дополнительные сведения см. в руководстве по Lua, глава 3.5 — Видимость Правила

Первый пример не работал, потому что переменная local func не видна до тех пор, пока не будет вычислено полное выражение. Вот почему первый пример ломается, пытаясь вызвать отсутствующую глобальную переменную.

Если вы хотите, чтобы эта функция была локальной, объявите переменную и затем назначьте ее. Нравиться:

local func
func = function ( n )
  if n == 1 then return 1
  else return n * func( n - 1 )
  end
end

print( func( 5 ) )

Вероятно, для этих случаев будет проще использовать синтаксический сахар Lua:

local function func( n )
  if n == 1 then return 1
  else return n * func( n - 1 )
  end
end

print( func( 5 ) )

Он будет переведен точно в ту же последовательность объявления переменной, а затем присваивания ей.

На этот раз переменная func будет видна новому function(n), поэтому она будет считывать значение для вызова из этого конкретного значения повышения.

Обратите внимание, что по-прежнему можно «сломать» функцию, назначив позже что-то другое в переменную func. Функции (как и любое другое значение) в Lua не имеют имен, они есть только у переменных. Таким образом, вызовы функций не компилируются жестко, значение функции для вызова всегда извлекается из переменной перед каждым вызовом.

person Vlad    schedule 11.01.2018
comment
Теперь мне ясно. Таким образом, при вызове локальной функции func() внутренняя функция func() по-прежнему ссылается на ее глобальную (неинициализированную) версию, хотя на момент вызова локальная функция func() уже существовала. Только что проверил это визуально, объявив глобальную функцию func() вместе с локальной версией, распечатав в них некоторые вспомогательные сообщения. Не думал об этом изначально. Спасибо за ответ. - person esgaldir; 13.01.2018

@Vlad хорошо отвечает на вопрос. Пара моментов по поводу вопроса для ясности:

  1. Функции - это значения, поэтому «глобальная функция», как указано в заголовке, не имеет смысла сама по себе без контекста вопроса.

  2. Связывание переменных — это процесс, посредством которого компилятор сопоставляет идентификатор в коде с переменной. Вопросы о связывании переменных обычно возникают в связи с рекурсивными функциями, но правила связывания проще и более общие. Они не имеют ничего общего с рекурсией, определениями функций или даже с локальными и глобальными. Тем не менее, синтаксический сахар для операторов определения функции может запутать проблему.

Например:

local x = x + 1 

x в выражении не является создаваемым x. Какая это переменная, зависит от любого кода выше. Это может быть локальная переменная, объявленная в блоке операторов или как параметр функции. Такое объявление может быть в том же блоке или определении функции или выше во внешнем. Глобальный — это всего лишь запасной вариант.

Очевидно, что если бы в приведенном выше примере не использовалось local, это же имя было бы связано с той же переменной.

 y = y + 1

Оба имени y являются одной и той же переменной, но, как указано выше, переменная определяется другим кодом.

Этот оператор определения функции аналогичен:

 function f() end

Если локальная f находится в области видимости, то это переменная, к которой компилятор привязывает «f», иначе «f» в глобальной среде.

person Tom Blodget    schedule 13.01.2018