Я люблю Lua. I love Lua.

Заполнение таблиц данными

Posted in Uncategorized by ilovelua on Январь 20, 2016

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

{
name = 'Name',
value = '',
type = 'string',
}

Соответственно, список параметров будет выглядеть как-то так:

{
	{
		name = 'Name',
		value = '',
		type = 'string',
	},
	{
		name = 'Order',
		value = 1,
		type = 'number',
	},
	{
		name = 'File',
		value = 'default.lua',
		type = 'string',
	},
}

Если параметров много, то для удобства заполнения я создаю функцию:

local function createParameter(name, value, type)
	return {
		name	= name,
		value	= value,
		type	= type,
	}
end

Список параметров начинает выглядеть так:

{
    createParameter('Name',     '',             'string'),
    createParameter('Order',    1,              'number'),
    createParameter('File',     'default.lua',  'string'),
}

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

local function Property(name)
    return {
        name    = name,
        
        Value = function(self, value)
            self.value = value
            
            return self
        end,
        
        Type = function(self, type)
            self.type = type
            
            return self
        end,
    }
end

Теперь заполнение таблицы параметров выглядит так:

{
    Property('Name')    :Value('')              :Type('string'),
    Property('Order')   :Value(1)               :Type('number'),
    Property('File')    :Value('default.lua')   :Type('string'),
}

Теперь каждый параметр подписан, а если есть опциональные поля, то их можно пропустить, а не писать такое:

createParameter('Name', '', nil, nil, nil, 'string'),

 

PS
Не помню, сам я это придумал, или где-то украл 🙂

Реклама
Tagged with: , ,

Lua 5.3 Руководство

Posted in Uncategorized by ilovelua on Ноябрь 13, 2015

Добавил руководство по Lua 5.3, переведенное Николаем Ведерниковым.
К сожалению, слетела оригинальная верстка. Кому надо, оригиналы лежат тут:

CHM

HTML

Tagged with: ,

Книжка по Lua на русском языке

Posted in Uncategorized by ilovelua on Май 20, 2014

Вышла книга Роберту Иерузалимски: Программирование на языке Lua

Подробнее: http://www.labirint.ru/books/433606/

Подробнее: http://www.ozon.ru/context/detail/id/26893654/

Подробнее: http://www.combook.ru/product/10780471/

Судя по описанию, это перевод третьего издания «Programming in Lua».

Избавление от циклических зависимостей модулей

Posted in Uncategorized by ilovelua on Март 14, 2014

Итак, у вас есть два модуля, каждый из которых использует другой.

ModuleA.lua:

local ModuleB	= require('ModuleB')

return {
	fun1 = function() print('~~~ModuleA fun1') end,
	fun2 = function() print('~~~ModuleA calls ModuleB.fun1()', ModuleB().fun1()) end,
}

ModuleB.lua:

local ModuleA	= require('ModuleA')

return {
	fun1 = function() print('~~~ModuleB fun1') end,
	fun2 = function() print('~~~ModuleB calls ModuleA.fun1()', ModuleA().fun1()) end,
}

В главной программе вы наивно пытаетесь их включить:

local ModuleA = require('ModuleA')
local ModuleB = require('ModuleB')

и получаете ошибку:

can't load module_test.lua: [string "ModuleB.lua"]:1: loop or previous error loading module 'ModuleA'

Как быть? Шаблон Медиатор приходит на помощь!

ModuleMediator.lua:

return {
	getModuleA = function() return require('ModuleA') end,
	getModuleB = function() return require('ModuleB') end,
}

Исправляем файлы модулей.
ModuleA.lua:

local ModuleMediator	= require('ModuleMediator')

return {
	fun1 = function() print('~~~ModuleA fun1') end,
	fun2 = function() print('~~~ModuleA calls ModuleB.fun1()', ModuleMediator.getModuleB().fun1()) end,
}

ModuleB.lua:

local ModuleMediator	= require('ModuleMediator')

return {
	fun1 = function() print('~~~ModuleB fun1') end,
	fun2 = function() print('~~~ModuleB calls ModuleA.fun1()', ModuleMediator.getModuleA().fun1()) end,
}

В главной программе все осталось по старому:

local ModuleA = require('ModuleA')
local ModuleB = require('ModuleB')

ModuleA.fun1()
ModuleA.fun2()

ModuleB.fun1()
ModuleB.fun2()

Запускаем:

~~~ModuleA fun1
~~~ModuleB fun1
~~~ModuleA calls ModuleB.fun1()
~~~ModuleB fun1
~~~ModuleA fun1
~~~ModuleB calls ModuleA.fun1()

Работает!
Я там маленько ошибся, когда вызывал функцию другого модуля, которая ничего не возвращает, внутри функции print(), но переписывать не хочется, извините.

Перехватчик для события удаления стейта.

Posted in Uncategorized by ilovelua on Февраль 11, 2014

Если Lua-библиотека, написанная на С, взаимодействует с несколькими Lua стейтами, то может потребоваться узнать о том, что какой-то из Lua стейтов удаляется. Сделать это можно так: при регистрации библиотеки в Lua стейте создаем безымянную userdata переменную, и у нее устанавливаем метатаблицу с методом __gc. Поскольку при удалении стейта у всех объектов вызываются финализаторы, то будет вызвана и эта функция безымянного объекта.

int onDestroyLuaState(lua_State* L)
{
	// делаем что-то полезное
	return 0;
}

const luaL_Reg libname[] = {
	// common functions
	{"Func1", func1},
	{"Func2", func2},
        ...
	{0, 0},
};

extern "C" __declspec(dllexport) int luaopen_libname(lua_State* L)
{
	lua_newuserdata(L, 0); // dummy for GC
	lua_createtable(L, 0, 1); // metatable
	lua_pushcfunction(L, onDestroyLuaState);
	lua_setfield(L, -2, "__gc");
	lua_setmetatable(L, -2);

	luaL_register(L, luaModuleName, libname);

	return 1;
}
Tagged with: ,

Новые и старые модули

Posted in Uncategorized by ilovelua on Январь 23, 2014

Это перевод вот этой статьи.

Создание и использование модулей

Есть два способа создания модулей — старый(и не рекомендуемый) для 5.0 и ранних версий 5.1, и новый для 5.1 и 5.2. Кроме нового способа мы также рассмотрим старый, поскольку вам могут встретиться программы, в которых он используется.

Создадим файл примера с следующим содержанием:

local mymodule = {}

function mymodule.foo()
    print("Hello World!")
end

return mymodule

Теперь, чтобы использовать этот модуль в интерактивном интерпретаторе, напишите:

> mymodule = require "mymodule"
> mymodule.foo()
Hello World!

В обычном файле скрипта рекомендуется сделать переменную mymodule локальной:

local mymodule = require "mymodule"
mymodule.foo()

Если мы сделаем переменную mymodule локальной в интерактивном интерпретаторе, то на следующей строке она выйдет из области видимости и мы не сможем ей воспользоваться. Поэтому мы должны сделать переменную mymodule глобальной.

Поскольку вы можете подключать один и тот же модуль в различных файлах, Lua кеширует модули в таблице package.loaded. Чтобы показать, как работает кеширование, изменим, например, функцию foo в mymodule.lua чтобы она печатала "Hello Module!". Если мы продолжим сессию в интерпретаторе, то произойдет следующее:

> mymodule = require "mymodule"
> mymodule.foo()
Hello World!

Чтобы действительно перезагрузить модуль, нужно сначала удалить его из кеша:

> package.loaded.mymodule = nil
> mymodule = require "mymodule"
> mymodule.foo()
Hello Module!

Еще одна приятная вещь — это возможность именовать модули произвольным образом, поскольку модули это обычные таблицы, сохраненные в переменной. Если название mymodule.lua покажется нам слишком длинным, мы можем его сократить:

> m = require "mymodule"
> m.foo()
Hello Module!

Другие способы создания модулей

Существуют различные способы создания модулей, и вы можете выбрать нужный вам в зависимости от задачи и ваших предпочтений.

Создать таблицу в начале скрипта и добавлять функции в нее:

local mymodule = {}

local function private()
    print("in private function")
end

function mymodule.foo()
    print("Hello World!")
end

function mymodule.bar()
    private()
    mymodule.foo() -- need to prefix function call with module
end

return mymodule

Сделать все функции локальными и добавить их в таблицу в конце скрипта:

local function private()
    print("in private function")
end

local function foo()
    print("Hello World!")
end

local function bar()
    private()
    foo() -- do not prefix function call with module
end

return {
  foo = foo,
  bar = bar,
}

Комбинация двух предыдущих способов:

local mymodule = {}

local function private()
    print("in private function")
end

local function foo()
    print("Hello World!")
end
mymodule.foo = foo

local function bar()
    private()
    foo()
end
mymodule.bar = bar

return mymodule

Вы даже можете изменить окружение блока кода(the chunk’s environment), чтобы сохранить в нем только необходимые глобальные переменные:

local print = print -- the new env will prevent you from seeing global variables

local M = {}
if setfenv then
	setfenv(1, M) -- for 5.1
else
	_ENV = M -- for 5.2
end

local function private()
    print("in private function")
end

function foo()
    print("Hello World!")
end

function bar()
    private()
    foo()
end

return M

Или, если вы не хотите сохранять глобальные переменные:

local M = {}
do
	local globaltbl = _G
	local newenv = setmetatable({}, {
		__index = function (t, k)
			local v = t[k]
			if v == nil then return globaltbl[k] end
			return v
		end,
		__newindex = M,
	})
	if setfenv then
		setfenv(1, newenv) -- for 5.1
	else
		_ENV = newenv -- for 5.2
	end
end

local function private()
    print("in private function")
end

function foo()
    print("Hello World!")
end

function bar()
    private()
    foo()
end

return M

Обратите внимание, что в этом случае доступ к глобальным переменным и переменным модуля может стать медленнее, поскольку используется функция __index. Пустая «прокси» таблица используется вместо таблицы _G(глобальное пространство имен) для того, чтобы не было возможности из модуля обращаться к глобальным переменным и не происходило следующее:
> require "mymodule"
> mymodule.foo()
Hello World!
> mymodule.print("example") -- unwanted __index metamethod
example

Старый способ создания модулей

В Lua 5.0 and 5.1 есть функция module, которая используется таким образом:
mymodule.lua:

module("mymodule", package.seeall)

function foo() -- create it as if it's a global function
    print("Hello World!")
end

И модуль может использоваться так:

> require "mymodule"
> mymodule.foo()
Hello World!

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

Из-за этого вы внутри модуля не видите глобальных переменных(например, print). Одно из решений — сохранить все необходимые глобальные переменные в локальных перед вызовом функции module, но это может быть утомительно, и решением было добавить второй параметр в функцию module, который должен быть функцией, в которую таблица модуля передается как параметр. package.seeall устанавливает для модуля метатаблицу с функцией __index, которая указывает на глобальное пространство имен, и благодаря этому модуль может обращаться к глобальным переменным. Проблема этого решения в том, что пользователь модуля может обращаться к глобальным переменным через модуль:

> require "mymodule"
> mymodule.foo()
Hello World!
> mymodule.print("example")
example

В лучшем случае это странно и неожиданно, и может быть дырой в безопасности(если вы используете модуль в защищенном(sandboxed) скрипте).
Причина, по которой не рекомендуется использовать функцию module, кроме тех, что приведены выше, это то, что пользователь модуля получает глобальную переменную с именем модуля, а так же то, что в Lua 5.2 функция не может изменять окружение своего вызывающего(по крайней мере без помощи отладочной библиотеки), что делает невозможным реализацию функции module.

Таблица package

Как было сказано ранее, Lua использует библиотеку package для управления модулями.

package.path (для модулей написанных на Lua) и package.cpath (для модулей написанных на C) это места, где Lua ищет модули.

> =package.path
./?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua;/usr/local/lib/lua/5.1/?.lua;/usr/local/lib/lua/5.1/?/init.lua;/usr/share/lua/5.1/?.lua;/usr/share/lua/5.1/?/init.lua
> =package.cpath
./?.so;/usr/local/lib/lua/5.1/?.so;/usr/lib/x86_64-linux-gnu/lua/5.1/?.so;/usr/lib/lua/5.1/?.so;/usr/local/lib/lua/5.1/loadall.so

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

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

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

Загрузка модулей

Posted in Uncategorized by ilovelua on Сентябрь 13, 2013

Если вы решили часть модулей вынести в отдельную папку, например:

./mylib/
./mylib/Module1.lua
./mylib/Module2.lua
./mylib/Module3.lua

то обращаться к ним из других модулей можно так:

local module1 = require('mylib.Module1')

Только нужно помнить, что в файле с модулем нужно также указывать относительный путь.
Например, в файле ./mylib/Module1.lua нужно написать:

module('mylib.Module1')

 

Вот здесь http://lua-users.org/wiki/ModulesTutorial написано, как нужно оформлять модули «по-новому».
Мы в своем проекте пока используем функцию module(). Возможно, вскоре мы от нее откажемся.

Tagged with: , ,

Итераторы

Posted in Uncategorized by ilovelua on Август 29, 2013

После того, как написал несколько раз такой код:

function(command, ...)
	...

	local combos = command.combos

	if combos then
		local deviceCombos = combos[deviceName]

		if deviceCombos then
			for i, combo in ipairs(deviceCombos) do
				-- что-то сделать с combo
				...
			end
		end
	end

	...
end

мне стало понятно, что нужно придумать что-нибудь поизящнее, чем эти двойные проверки. И тут я вспомнил про итераторы! Programming in Lua говорит нам, что итератором может быть функция, которая при каждом вызове возвращает следующий элемент коллекции. Поскольку deviceCombos это массив, а мне при переборе индекс элемента не нужен, то задача упрощается:

function commandCombos(command, deviceName)
	local pos = 0
	local combos
	local deviceCombos

	if command then
		combos = command.combos

		if combos then
			deviceCombos = combos[deviceName]
		end
	end

	return function()
		if deviceCombos then
			pos = pos + 1
			return deviceCombos[pos]
		end
	end
end

Обратите внимание, что переменная pos — это upvalue.
Теперь проверим получившуюся функцию:

local commandNil = nil
local commandNoCombos = {}
local commandEmptyCombos = {
	combos = {},
}
local command = {
	combos = {
		mouse = {
			{key = 'MOUSE1', action = 1},
			{key = 'MOUSE2', action = 2},
		},
		keyboard  = {
			{key = 'A', action = 11},
			{key = 'B', action = 22},
		},
	}
}

for combo in commandCombos(commandNil) do
	assert(false)
end

for combo in commandCombos(commandNoCombos) do
	assert(false)
end

for combo in commandCombos(commandNoCombos) do
	assert(false)
end

for combo in commandCombos(commandEmptyCombos) do
	assert(false)
end

for combo in commandCombos(command, 'mouse') do
	print('~~~mouse', combo.key, combo.action)
end

for combo in commandCombos(command, 'keyboard') do
	print('~~~keyboard', combo.key, combo.action)
end

На выходе ожидаемо получаем:

~~~mouse MOUSE1 1
~~~mouse MOUSE2 2
~~~keyboard A 11
~~~keyboard B 22

Скорость цикла for

Posted in Uncategorized by ilovelua on Август 8, 2013

В чужом коде увидел такое:

for i = 1, #data.values do
	local value = data.values[i]
	...
end

И тут же осудил внутри себя этого нерадивого разработчика «Экий ты, брат, ленивец! Нет бы написать «правильно»!»:

for i, value in ipairs(data.values) do
	...
end

И тут меня сомнение взяло: второй вариант мне лично кажется красивее и правильнее, но быстр ли он? И быстро был написан тест:

local data = {
	values = {}
}

for i = 1, 10000 do
	table.insert(data.values, i)
end

local function simpleForTest()
	local j
	local t = os.clock()

	for k = 1, 100 do
		for i = 1, #data.values do
			j = data.values[i]
		end
	end

	print('~~~#', os.clock() - t)
end

local function ipairsTest()
	local j
	local t = os.clock()

	for k = 1, 100 do
		for i, val in ipairs(data.values) do
			j = val
		end
	end

	print('~~~ipairs', os.clock() - t)
end

for i = 1, 10 do
	simpleForTest()
	ipairsTest()
end

А результаты он давал такие:
~~~# 0.063
~~~ipairs 0.121
~~~# 0.063
~~~ipairs 0.122
~~~# 0.063
~~~ipairs 0.122
~~~# 0.062
~~~ipairs 0.121
~~~# 0.063
~~~ipairs 0.122
~~~# 0.063
~~~ipairs 0.122
~~~# 0.063
~~~ipairs 0.122
~~~# 0.062
~~~ipairs 0.122
~~~# 0.063
~~~ipairs 0.122
~~~# 0.063
~~~ipairs 0.122

То есть «некрасивый» код работает в два раза быстрее «красивого». Вот так.

Upvalues и вопросы производительности

Posted in Uncategorized by ilovelua on Июль 23, 2013

Дано: список событий(массив таблиц).
Задача: с делать фильтр по полям событий.

Решение 1(прямое, как палка):

function getEventVisible(event, filter)
	if filter.initiatorId then
		if filter.initiatorId ~= event.initiatorMissionID then
			return false
		end
	end

	if filter.targetId then
		if filter.targetId ~= event.targetMissionID then
			return false
		end
	end

	if filter.objectId then
		if event.initiatorMissionID ~= filter.objectId and event.targetMissionID ~= filter.objectId then
			return false
		end
	end

	if filter.weaponId then
		if filter.weaponId ~= event.weapon then
			return false
		end
	end

	if filter.eventType then
		if filter.eventType ~= eventEventType then
			return false
		end 
	end

	if filter.initiatorCoalition then
		if filter.initiatorCoalition ~= DebriefingMissionData.getUnitCoalitionName(event.initiatorMissionID) then
			return false
		end
	end

	if filter.targetCoalition then
		if filter.targetCoalition ~= DebriefingMissionData.getUnitCoalitionName(event.targetMissionID) then
			return false
		end
	end

	return true
end

Тут мы видим, что некоторые поля используются несколько раз. Это срочно нужно оптимизировать:

function getEventVisible2(event, filter)
	local initiatorId = filter.initiatorId
	local targetId = filter.targetId
	local objectId = filter.objectId
	local weaponId = filter.weaponId
	local eventType = filter.eventType
	local initiatorCoalition = filter.initiatorCoalition
	local targetCoalition = filter.targetCoalition
	local getUnitCoalitionNameFunc = DebriefingMissionData.getUnitCoalitionName
	local eventInitiatorId = event.initiatorMissionID
	local eventTargetId = event.targetMissionID
	local eventWeaponId = event.weapon
	local eventEventType = event.type
	local eventInitiatorCoalition = getUnitCoalitionNameFunc(eventInitiatorId)
	local eventTargetCoalition = getUnitCoalitionNameFunc(eventTargetId)

	if initiatorId then
		if initiatorId ~= eventInitiatorId then
			return false
		end
	end

	if targetId then
		if targetId ~= eventTargetId then
			return false
		end
	end

	if objectId then
		if eventInitiatorId ~= objectId and eventTargetId ~= objectId then
			return false
		end
	end

	if weaponId then
		if weaponId ~= eventWeaponId then
			return false
		end
	end

	if eventType then
		if eventType ~= eventEventType then
			return false
		end 
	end

	if initiatorCoalition then
		if initiatorCoalition ~= eventInitiatorCoalition then
			return false
		end
	end

	if targetCoalition then
		if targetCoalition ~= eventTargetCoalition then
			return false
		end
	end

	return true
end

Тут можно было бы откинуться на кресле и сделать глоток крепкого виски… Но чу! Это что же такое получается? Для фильтра, который один для всех событий, значения полей каждый раз извлекаются из таблицы filter. Непорядок. А ну-ка, где тут у нас upvalues?

function getFilterFunc(filter)
	local initiatorId = filter.initiatorId
	local targetId = filter.targetId
	local objectId = filter.objectId
	local weaponId = filter.weaponId
	local eventType = filter.eventType
	local initiatorCoalition = filter.initiatorCoalition
	local targetCoalition = filter.targetCoalition
	local getUnitCoalitionNameFunc = DebriefingMissionData.getUnitCoalitionName

	return function(event)
		local eventInitiatorId = event.initiatorMissionID
		local eventTargetId = event.targetMissionID
		local eventWeaponId = event.weapon
		local eventEventType = event.type
		local eventInitiatorCoalition = getUnitCoalitionNameFunc(eventInitiatorId)
		local eventTargetCoalition = getUnitCoalitionNameFunc(eventTargetId)

		if initiatorId then
			if initiatorId ~= eventInitiatorId then
				return false
			end
		end

		if targetId then
			if targetId ~= eventTargetId then
				return false
			end
		end

		if objectId then
			if eventInitiatorId ~= objectId and eventTargetId ~= objectId then
				return false
			end
		end

		if weaponId then
			if weaponId ~= eventWeaponId then
				return false
			end
		end

		if eventType then
			if eventType ~= eventEventType then
				return false
			end 
		end

		if initiatorCoalition then
			if initiatorCoalition ~= eventInitiatorCoalition then
				return false
			end
		end

		if targetCoalition then
			if targetCoalition ~= eventTargetCoalition then
				return false
			end
		end

		return true
	end
end

Ну а теперь…(ТЕСТЫ!!! ТЕСТЫ!!!)
Я не вижу ваши руки!

local events = DebriefingEventsData.getEvents()
local filterFunc = getFilterFunc(filter_)

print('~~~event count:', #events)

local t = os.clock()

for i = 1, 1000 do
	local result = {}
	for i, event in ipairs(events) do	
		if getEventVisible(event, filter_) then
			table.insert(result, event)
		end
	end
end

print('~~~getEventVisible', os.clock() - t)

t = os.clock()

for i = 1, 1000 do
	local result = {}
	for i, event in ipairs(events) do	
		if getEventVisible2(event, filter_) then
			table.insert(result, event)
		end
	end
end

print('~~~getEventVisible2', os.clock() - t)

t = os.clock()

for i = 1, 1000 do
	local result = {}
	for i, event in ipairs(events) do	
		if filterFunc(event) then
			table.insert(result, event)
		end
	end
end

print('~~~filterFunc', os.clock() - t)

Результаты:
~~~event count: 1467
~~~getEventVisible 1.32
~~~getEventVisible2 2.583
~~~filterFunc 2.286
~~~event count: 1467
~~~getEventVisible 0.698
~~~getEventVisible2 2
~~~filterFunc 1.706
~~~event count: 1467
~~~getEventVisible 1.326
~~~getEventVisible2 2.581
~~~filterFunc 2.284
~~~event count: 1467
~~~getEventVisible 1.642
~~~getEventVisible2 2.203
~~~filterFunc 1.984
~~~event count: 1467
~~~getEventVisible 1.506
~~~getEventVisible2 2.061
~~~filterFunc 1.822
~~~event count: 1467
~~~getEventVisible 1.338
~~~getEventVisible2 2.577
~~~filterFunc 2.275
~~~event count: 1467
~~~getEventVisible 0.75999999999999
~~~getEventVisible2 2.013
~~~filterFunc 1.712
~~~event count: 1467
~~~getEventVisible 1.322
~~~getEventVisible2 2.576
~~~filterFunc 2.281
~~~event count: 1467
~~~getEventVisible 1.323
~~~getEventVisible2 2.574
~~~filterFunc 2.288
~~~event count: 1467
~~~getEventVisible 1.687
~~~getEventVisible2 2.234
~~~filterFunc 1.999
~~~event count: 1467
~~~getEventVisible 1.717
~~~getEventVisible2 2.087
~~~filterFunc 1.808
~~~event count: 1467
~~~getEventVisible 1.743
~~~getEventVisible2 2.109
~~~filterFunc 1.83

Ой, что это? Наш прекрасный оптимизированный код работает хуже неоптимизированного? Похоже, что так. Причем местами «неоптимизированная» версия быстрее «оптимизированной» почти в 2 раза!
Думаю, что в getEventVisible2() создается слишком много локальных переменных, причем некоторые из них могут не понадобиться. А что не так с filterFunc()? Там вроде локальных переменных совсем немного? Похоже, что доступ к «замороженным» переменным не так уж и быстр, либо Lua при вызове этой функции должна откуда-то загрузить все upvalues.
В общем, как обычно с оптимизацией — все нужно проверять.