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

Fingercomp's Playground

  • записи
    93
  • комментария
    373
  • просмотров
    222 675
Fingercomp

Обновлено 2019-07-10. Отревёрсил коллапс кода в одну строку.

Сообщение добавил Fingercomp

Пара трюков OpenComputers

Fingercomp

2 325 просмотров

Здесь опишу такие штучки, которые могут потребоваться продвинутым OC-программистам (да и просто Луа-программистам).

 

Busy Idle
С помощью этого трюка можно делать довольно точные задержки, причём с длительностью менее тика.

local lastSleep = os.clock()

local function sleep(t)
  local begin = os.clock()
  
  while os.clock() - begin < t do
    if lastSleep - os.clock() >= 3.5 then  -- В конфигурации дефолтное значение = 5 секунд, ставим на 1.5 меньше для безопасности.
      os.sleep(0.05)  -- Вынужденная задержка.
      lastSleep = os.clock()
      t = t - 0.05
    end
  end
end


Проверка по значению
Очень часто в моих программах нужно найти ключ, значение которого соответствует данному. Для этого я написал простую функцию:

local function isin(tbl, value)
  for k, v in pairs(tbl) do
    if v == value then
      return true, k
    end
  end
  
  return false
end

На огромных массивах может и затормозить — скорость работы прямо зависит от длины массива.

 

Табличная магия
Рассмотрим этот на первый взгляд обычный пример кода:

local tbl1 = {"My", "super", "table", 42}
local tbl2 = tbl1

tbl2[2] = "cool"

for _, tbl in pairs({tbl1, tbl2}) do -- Напечатать значения таблиц
  for k, v in pairs(tbl) do
    print(k, v)
  end
end

Разумно ожидать такое:

1 My
2 super
3 table
4 42
1 My
2 cool
3 table
4 42
Но вместо этого получаем:
1 My
2 cool
3 table
4 42
1 My
2 cool
3 table
4 42


Как видно, изменив значение в одной таблице, изменилась и другая.
Дело в том, что переменная хранит указатель на таблицу, а не саму таблицу. Соответственно, и tbl1, и tbl2 ссылаются на один и тот же массив.
На первый взгляд это кажется ненормальным. Как скопировать-то таблицу?

local function copy(tbl)
  if type(tbl) ~= "table" then
    return tbl
  end
  
  local result = {}
  
  for k, v in pairs(tbl) do
    result[k] = copy(v)
  end
  
  return result
end


Но из этого можно извлечь очень полезное применение. Когда мы передаём таблицу в аргументы функции, массив не копируется, а даётся указатель на тот же самый. Поэтому можно сообразить такой код:

local function removeOddNums(tbl)
  for k, v in pairs(tbl) do
    if tonumber(v) and v % 2 == 1 then
      tbl[k] = nil
    end
  end
end

local table = {5, 26, 249586, 457139, 876, 42, 153}
removeOddNums(tbl)

И он будет работать. Этим и объясняется, почему table.sort не возвращает таблицу. У меня не самое полезное применение, однако с помощью таблицы можно создавать "поинтеры", например, так: local numPtr = {42}, а в функциях использовать так: local value = numPtr[1]; numPtr[1] = 666. И уже использовать их в своих вычислениях.

 

Думаю, вы найдёте применение этим фокусам. Не самые очевидные моменты, однако иногда требуется.
The end.

  • Нравится 4


9 комментариев


Рекомендуемые комментарии

Оу, классно! Не ожидал от Lua возможности даже имитировать указатель на переменную.

Но у меня сомнения относительно os.clock(). Всегда думал, что это время, затраченное процессором на выполнение кода, а оно совпадает с реальным временем лишь в случае, когда процессор 100% своего времени тратит на выполнение кода именно на этом компьютере. Что-то типа активной части uptime.

Поделиться комментарием


Ссылка на комментарий

Оу, классно! Не ожидал от Lua возможности даже имитировать указатель на переменную.

Но у меня сомнения относительно os.clock(). Всегда думал, что это время, затраченное процессором на выполнение кода, а оно совпадает с реальным временем лишь в случае, когда процессор 100% своего времени тратит на выполнение кода именно на этом компьютере. Что-то типа активной части uptime.

Верно, правда, почему-то решил, что в machine.lua используется время компьюетра. Но нет.

В конфигурации OpenComputers есть настройка timeout, равная 5 реальным (не игровым) секундам по умолчанию. Обновил код.

Поделиться комментарием


Ссылка на комментарий

Пришлось вернуться к os.clock: аптайм давал неточное время.

Мы можем здесь положиться на клок, так как этот код не будет делать yield во время слипа, только при вынужденном торможении (и тогда разность будет незначительна).

Поделиться комментарием


Ссылка на комментарий

Пришлось вернуться к os.clock: аптайм давал неточное время. Мы можем здесь положиться на клок, так как этот код не будет делать yield во время слипа, только при вынужденном торможении (и тогда разность будет незначительна).

На практике же выходит значительная разница

c0=os.clock() t0=computer.uptime() for i=1,10^7 do end print(os.clock()-c0,computer.uptime()-t0)
-- 2.92   2.94
c0=os.clock() t0=computer.uptime() for i=1,1000 do os.sleep(0.05) end print(os.clock()-c0,computer.uptime()-t0)
-- 2.37  71.4
Само по себе использование os.sleep(0.05) неявно делает yield.

Поделиться комментарием


Ссылка на комментарий

Там даже ещё круче завернули.

os.sleep => event.pull => repeat computer.pullEvent until deadline

Как же тут хромает задержка, ох. Так что os.clock определённо.

Поделиться комментарием


Ссылка на комментарий

Да, я видел, как там завернуто. Но это как раз таки понятно: задержка – лучшее время для обработки событий. В пределах тика задержка вполне себе удовлетворительная, если обработчики событий не ресурсоемкие.

К задержам без sleep (без yield) я отношусь плохо, т. к. это шаг к лагодрому. Но если задержка длится миллисекунды и используется раз в минуту, то почему бы и нет.

Поделиться комментарием


Ссылка на комментарий

Ну это верно.

Мне такая задержка потребовалась для плеера: там тик длиться может 1/720 секунды, а то и больше. Разумно использовать здесь busy-idle. Что я и сделал.

Поделиться комментарием


Ссылка на комментарий

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

Поделиться комментарием


Ссылка на комментарий

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

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

Гость
Добавить комментарий...

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

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

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

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

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

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