Перейти к публикации
Форум - ComputerCraft
  • 0
qwertyMAN

Проблема с переназначением фукнций

Вопрос

Я тут занялся переписывать в очередной раз свою игру. Решил структуризировать всё по блокам и прогружать их поочередно через require. А для дебага выкладывать при этом инфу о том что прогружаю в консоль. (движок love2d) Чтобы в случае чего понимать на каком этапе, в каком блоке у меня возникли ошибки. Да и просто мне интересны такие мелочи.

 

В общем к сути проблемы, я хотел написать так:

require = function(path)
	print(path)
	require(path)
end

Вспомнил, что lua достаточно лояльный язык программирования, позволяет выражения по типу

x, y = y, x

Или

x = x + 1

Поэтому я подумал, что и тут глобальная функция будет просто переназначена. Но в итоге при первом вызове require, функция была зациклена, а точнее вошла в рекурсию.

 

Я понимал, что в lua переменная require - только ссылка на функцию. И решение пришло очень быстро, переместить временно ссылку на функцию в новую переменную, которая будет вскоре после создания нужной нам функции удалена.

Вышло вот как:

do
	local r = require
	require = function(path)
		print(path)
		r(path)
	end
end

Да, проблема была решена и очень быстро. Но вот вопрос о том, почему появилась рекурсия для меня всё ещё не ясен. Видимо не достаточно хорошо знаю lua. Прошу объяснить, как такое могло получится.

 

Вопросы, которые я хочу здесь обсудить:

  1. Почему первый вариант оказался не правильным и как шаг за шагом действовал интерпретатор при таком варианте кода
  2. Какие ещё способы прогрузки кода из файлов есть? Я слышал кажется про функцию load(). Если есть альтернатива require, какие с ней отличия? Работают ли там локальные переменные созданные из основного файла, откуда была вызывана функция?
  3. В чём различия глобальной переменной от локальной? (я много работал с локальными переменными, но глобальные для меня стали просто чем-то неизведанным и страшным, куда я боюсь лесть. Хотя минусы функции require заставляют отказаться от локальных переменных и перейти на глобальные)

Заранее спасибо за ответы. Благодаря форуму и его обитателям, я подтянул свои знания в lua с уровня быдлокодера, до написания читаемого кода.

Изменено пользователем qwertyMAN

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Рекомендованные сообщения

  • 0

Первый вариант:

require = function(path)
    print(path)
    require(path)
end

Ты берешь глобальную переменную, отвязываешь от старой реализации, от старой функции (которая при этом попадает в сборщик мусора, т.к. нигде не сохранилось других ссылок на нее) и привязываешь к новой.

И, разумеется, в тот момент, когда ты сделаешь свой вызов require(), ты обратишься к новой, своей функции. И когда эта новая функция в свою очередь сделает вызов require() она тоже обратится к новой функции - то есть к себе. Рекурсия.

 

Второй вариант:

local r = require
require = function(path)
    print(path)
    r(path)
end

Здесь ты добавил еще один указатель на объект функции, на который изначально ссылается переменная require.

Таким образом, когда ты переадресовал саму переменную на новый объект, старая реализация не была удалена - на нее ведет еще одна ссылка - переменная r.

И именно через эту ссылку твоя новая функция может вызывать старую функцию.

Здесь все нормально и логично.

  • Like 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
  • 0

@@Totoro наверное это уже будет придиркой, но интересно, а почему же эта конструкция не удаляет ссылку на переменную:

require = require

Я думал, что функция по такому же принципу будет хранить облачно данные из переменной и внутрь их засунет.

Для меня просто стало открытием, что это не работает в функциях.

Хотя если бы я пользовался привычными и понятными локальными переменными, такой бы ошибки не было бы. Всё же отличная штука эти локальные переменные.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
  • 0

@@Totoro наверное это уже будет придиркой, но интересно, а почему же эта конструкция не удаляет ссылку на переменную:

require = require

Я думал, что функция по такому же принципу будет хранить облачно данные из переменной и внутрь их засунет.

Для меня просто стало открытием, что это не работает в функциях.

Хотя если бы я пользовался привычными и понятными локальными переменными, такой бы ошибки не было бы. Всё же отличная штука эти локальные переменные.

Потому что ссылочка остаётся, разве нет? При a = a ты же не будешь ожидать, что значение куда-то внезапно пропадёт?

 

Сборщик мусора — это умная вещь, не надо его недооценивать.

 

А объяснение Тоторы я б хотел дополнить ещё комментарием: тело функции, заданной в форме function NAME(...) ... end, будет иметь ссылку на саму себя. Это позволяет тебе очень просто делать рекурсивные функции.

local function fib(n)
  local function iter(acc, i)
    if i <= 0 then
      return acc
    else
      return iter(i * acc, i - 1)
    end
  end

  return iter(1, n)
end

Код типа local a = function() a() end уже не рекурсия: он будет проинтерпретирован как local function a() _ENV.a() end.

Но код a = function() a() end — это _G.a = function() _G.a() end, то есть ссылочка будет. Если не будешь трогать _G.a.

 

Что, впрочем, не означает, что рекурсия возможна только при наличии внутри функции ссылки на себя.

local function recurse(g)
  return (function(f)
            return g(function(x)
                       return f(f)(x)
                     end)
          end)(function(f)
                 return g(function(x)
                            return f(f)(x)
                          end)
               end)
end

print(recurse(function(rec)
                return function(n)
                  if n == 0 then
                    return 1
                  else
                    return n * rec(n - 1)
                  end
                end
              end)(10))
--> 3628800

Но я надеюсь, что для тебя этот код будет тоже похож на макароны с кетчупом.

Изменено пользователем Fingercomp

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
  • 0

 

 

Но я надеюсь, что для тебя этот код будет тоже похож на макароны с кетчупом

Нельзя было скинуть рабочий код?

Там ошибка в этой строке у меня

return n * rec(n - 1)

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
  • 0

Нельзя было скинуть рабочий код?

Не повелите казнить, Ваше величество!

 

...Код поправил, да.

  • Like 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Создайте аккаунт или войдите в него для комментирования

Вы должны быть пользователем, чтобы оставить комментарий

Создать аккаунт

Зарегистрируйтесь для получения аккаунта. Это просто!

Зарегистрировать аккаунт

Войти

Уже зарегистрированы? Войдите здесь.

Войти сейчас

×