Перейти к содержимому
  • 0
qwertyMAN

Как переназначить функцию require?

Вопрос

Я тут занялся переписывать в очередной раз свою игру. Решил структуризировать всё по блокам и прогружать их поочередно через 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

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


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

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

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

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

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

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

 

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

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

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

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

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

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

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


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

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

require = require

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

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

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

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


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

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

Суть вопроса такая. Дан файл. Нужно в начале программы выполнить прогрузку по типу

require("settings")

Ну скажем так, это файл с настройками.

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

Значит при первом прочтении файла мы читаем первый "блок" данных, потом на основе их прогружаются другие файлы, далее читается следующий "блок" настроек, потом зависимые от них файлы прогружаются и ещё раз прогружается файл настроек.

То есть мне нужно несколько раз обращаться к одному файлу и по частям его считывать. Такое реализуемо в lua?

 

Ещё у меня есть непонимание того, как устроены зависимости. Как интерпретатор работает. Есть годные, краткие гайды по работе интерпретатора?

Вот например у меня намешано несколько функций. Одна зависит от другой. Например нижняя от верхней, а верхняя от нижней. И возникает вопрос, какая функция за какой должна следовать. Как именно работают зависимости? Есть ли решение в каком порядке написать две функции которые друг от друга зависят, чтобы они работали не выдавая ошибки?

Скорее всего это тупой вопрос. Но вызван он не пониманием того, как работает интерпретатор. Объясните по шагам, пожалуйста. Я джва года этого ждал.

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

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


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

@@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

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


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

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

  • Аргумент функции мы отдаём в резолвер, который возвращает найденный по данному пути файл.
  • Файл обрабатываем через load или loadfile.

Как видно, всё исполнение кода осуществляется лишь этими функциями (хотя есть ещё и dofile, к слову). Если интересно про них узнать, посылаю в мануал. А как практический пример рекуомендую изучить сырцы /lib/require.lua в OpenOS.

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

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


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

Есть ли решение в каком порядке написать две функции которые друг от друга зависят, чтобы они работали не выдавая ошибки?

 

 

Определить функции наперед, или засунуть в таблицу.

 

 

 

local a, b
 
function a()
  ...
end
 
function b()
  ...
end

 

 

 

или:

 

 

 

local t = {}
 
function t.a()
  ...
end
 
function t.b()
  ...
end

 

 

 

Функции определяются так же, как и любые другие переменные, и подвергаются областям видимости.

local a = 5
-- тут доступна переменная a, но не b
 
do
  local c = a * 2
  -- тут доступна переменная a и c
end
 
local b = a
-- тут и a, и b, но не c
Изменено пользователем LeshaInc

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


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

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

Суть вопроса такая. Дан файл. Нужно в начале программы выполнить прогрузку по типу

require("settings")

Ну скажем так, это файл с настройками.

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

package.loaded["settings"] = nil

require("settings")

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


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

 

 

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

 

Для таких целей лучше использовать dofile.

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


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

Присоединяйтесь к обсуждению

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

Гость
Ответить на вопрос...

×   Вы вставили отформатированное содержимое.   Удалить форматирование

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отобразить как ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.


×
×
  • Создать...