Страницы

Поиск по вопросам

Показаны сообщения с ярлыком lua. Показать все сообщения
Показаны сообщения с ярлыком lua. Показать все сообщения

суббота, 11 апреля 2020 г.

Когда точно в Lua массив (таблица только с array-part) приобретает hash-part (становится hash)?

#lua

                    
Необходимо передавать таблицы (tables) в Lua коде по сети (компьютерная мультиплеерная
игра). В связи с чем очень актуален вопрос о размерах передаваемых данных (о сохранении
их как можно меньшими).
Для сохранения структуры таблицы и "удержания" её в array-like стиле рекомендуют
использовать table.insert, table.remove... 
Имеется код (сори за форматирование, не работает в браузере):
local var = {}

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

var[10000] = { var = 1, some = "string", another = {} }
var[555] = false
var[1] = 1
var[2] = {}
var[1] = {}

Вопросы:

Останется ли таблица var массивом ( таблицей только c array part ), или преобразуется
в hash ( таблицей с двумя частями array и hash ). ( Сам думаю ответ "останется только
с array-part",  так как структура таблицы не затронута - затронуты лишь значения элементов,
но очень сомневаюсь в этом ответе, а нужно знать наверняка ).
Почему? :) Как это проверить?

Предельно уточняя: если таблица изначально имеет только array-part, то приведет ли
изменение значения элемента таблицы напрямую через синтаксис var[1] = {} var[2] = false
var[3] = 1 и т.п. к изменению структуры таблицы и появлению у ней hash-part? ( При
условии что заранее дано:)
local var = {}
for i = 1, 10000, 1 do
    table.insert ( var, i )
end


Подозреваю, что ответ для кода выше "нет", но:
var[10001] = 1

Уже затронет структуру таблицы и создаст "накладные расходы" по памяти.
В связи с чем думаю ответ таков ( для кода выше) : до n <= 10000 код var[n] = {}
не затрагивает структуру таблицы,  а значит не ведет к созданию hash-part ( таблица
остается array-like). Однако код var[n + 1] = {} приведет к накладным расходам памяти
(подробнее тут http://www.lua.org/gems/sample.pdf )
Ответ правильный или нет?
Спасибо!    


Ответы

Ответ 1



Суть в том что в Lua есть только ассоциативные таблицы "ключ-значение". Она может только притворятся списком. Посему arr['1'] = 1; arr['1000000'] = 1 не значит что таблица разбухнет аж на 999999 записей.

суббота, 21 марта 2020 г.

Удалить пустые элементы массива

#массивы #lua


Есть массив ITEMS(item1=1,item2=2,item4=4,"",item10=10,"",item16=16)

Вывожу его содержимое 
print("массив ITEMS:",table.concat (ITEMS, ", "))

Получаю: 
1, 2, 4, , 10, , 16

Как можно удалить пустые строки отсюда?
    


Ответы

Ответ 1



Ответ @Ainar-G работает некорректно для массивов, где два пустых элемента идут подряд. Есть два способа обойти это: Сперва записать элементы для удаления, а после уже удалить их: local a = { "a", "b", "", "c", "", "", "d" } local function cleanupArray(arr, valtodel) local todelete = {} for k, v in ipairs(arr) do if v == valtodel then todelete[#todelete+1] = k end end for k, v in ipairs(todelete) do table.remove(arr, v-k+1) end return arr end cleanupArray(a, "") print(table.concat(a, ", ")) Try it online! Не менять индекс, если мы удалили элемент: local a = { "a", "b", "", "c", "", "", "d" } local function cleanupArray(arr, valtodel) for i = 1, #arr do ::begin:: local v = arr[i] if v == nil then return end if v == valtodel then table.remove(arr, i) goto begin end end end cleanupArray(a, "") print(table.concat(a, ", "))

воскресенье, 8 марта 2020 г.

Как получить то, что было задано с define в lua FFI?

#lua


Многие функции системного программирования в linux работают с флагами (например,
O_NONBLOCK). Но эти флаги же надо ещё и получить! Насколько я понимаю, флаги в заголовочных
файлах определены при помощи #define. Традиционное нечто вроде ffi.C.O_NONBLOCK выдаёт
ошибку, это не функция и не глобальная переменная! При этом файл заголовков подгружен
при помощи библиотеки lcpp. И как же получить эти самые define'ы?
    


Ответы

Ответ 1



Надо использовать не ffi.C. а ffi.lcpp_defs.. Пример 1 Файл test1.lua: local lcpp = require("lcpp") local ffi = require("ffi") ffi.cdef([[ #define MAX_SIZE 100 typedef struct { int data[MAX_SIZE]; } test_t; ]]) print("MAX_SIZE = " .. ffi.lcpp_defs.MAX_SIZE) Результат работы: MAX_SIZE = 100 Пример 2 Файл test2.lua: local lcpp = require("lcpp") local ffi = require("ffi") ffi.cdef("#include \"test.h\"") print("FREQUENCY_MHZ = " .. ffi.lcpp_defs.FREQUENCY_MHZ) print("FREQUENCY_KHZ = " .. ffi.lcpp_defs.FREQUENCY_KHZ) print("_PACKED_ = " .. ffi.lcpp_defs._PACKED_) Файл test.h: #ifndef TEST_H #define TEST_H #define FREQUENCY_MHZ 2 #define FREQUENCY_KHZ (FREQUENCY_MHZ * 1000) #define _PACKED_ __attribute__((packed)) #endif // TEST_H Результат работы: FREQUENCY_MHZ = 2 FREQUENCY_KHZ = (FREQUENCY_MHZ * 1000) _PACKED_ = __attribute__((packed)) Всё в общем-то работает, но из второго примера видно, что полного препроцессинга define'ов не происходит. На самом деле тут всё не так плохо. Пример 3 Файл test3.lua: local ffi = require("ffi") local lcpp = require("lcpp") local data, state = lcpp.compile([[ #define ROW_CNT 0x0F #define COL_CNT 0x0A #define DATA_CNT (ROW_CNT * COL_CNT) int data[DATA_CNT]; ]]) local data_type = ffi.typeof(data) print("data = " .. data) print("ROW_CNT = " .. state.defines.ROW_CNT) print("COL_CNT = " .. state.defines.COL_CNT) print("DATA_CNT = " .. state.defines.DATA_CNT) print("typeof(data) = " .. tostring(data_type)) Результат работы: data = int data[(15*10)]; ROW_CNT = 0x0F COL_CNT = 0x0A DATA_CNT = (ROW_CNT * COL_CNT) typeof(data) = ctype Как видно из этого примера, размерность массива вычислена правильно (150 элементов).

Лист переменных из массива

#lua


Можно ли на lua создать лист переменных из массива?

R, G, B = getColors()




function getColors()
    return {255, 128, 0}
end

    


Ответы

Ответ 1



Используйте функцию table.unpack: R, G, B = table.unpack(getColors())

Ответ 2



Разобрался. Просто надо было возвращать переменные не массивом, а через запятую R, G, B = getColors() function getColors() return 255, 128, 0 end

понедельник, 24 февраля 2020 г.

Как правильно поместить метод в таблицу?

#lua


Вот такой пример не работает, в нём при вызове функции self == nil:

function test()
  return {
    foo = "bar",
    say = function()
      print(self.foo)
    end
  }
end


А вот так всё работает, но такая запись мне не нравится ):

function test()
  local result = {foo = "bar"}
  function result:say()
    print(self.foo)
  end
  return result
end


Проверка:

local t = test()
t:say()

    


Ответы

Ответ 1



У функции должен быть параметр self: function test() return { foo = "bar", say = function(self) print(self.foo) end } end Вызовы вида t:say() на самом деле всегда разворачивается в t.say(t) и если вы хотите внутри функции say() иметь доступ к полям таблицы, вам нужно объявить как минимум один параметр (с любым именем), в который вам и будет передана эта таблица. В Lua, общепринятое имя для такого параметра - self, но вы можете использовать любое. Если у функции есть несколько параметров, то таблица будет всегда передаваться в качестве "нулевого" параметра, т.е вызов t:say(x, y) будет транслироваться в t.say(t, x, y) и при объявлении функции вы всегда должны ожидать параметр self на первом месте: say = function(self, x, y) print(self.foo) end Что касается синтаксиса с двоеточием, то это т.н. синтаксический сахар. В Lua на самом деле нету классов, и их можно лишь эмулировать (в некоторой степени) при помощи таблиц. И чтобы эта эмуляция выглядела похожей, на использование классов, и был введён такой синтаксис.

суббота, 22 февраля 2020 г.

Lua, функциональное программирование

#массивы #алгоритм #функции #lua


Дана задача: в массиве чисел (допустим 1 2 3 4 5 6 7 8 9) реверсировать числа на
нечетных позициях (тут это 1,3,5,7,9, должно получится 9 2 7 4 5 6 3 8 1).
Все это сделать с помощью функционального программирования:
Запрещено использовать циклы, условные операторы, операторы присваивания, операторы
контроля (return можно). Все делать с помощью рекурсий и вызовов других функций.

Вот что я набросал:  

local function main()
array = {};
modArray = {};

--Finding count of nums
file = io.open("nums.txt", "r");
numsArrayString = file:read();

stofSpaces = string.gsub(numsArrayString, "%d", "")
ctofNums = string.len(stofSpaces);
file:close();

--Filling array
file = io.open("nums.txt", "r");
fillArray(array, 0);
file:close();

--1st part
irEnd(array, modArray, 0);
end

function fillArray(array, i)
array[i] = file:read("*n");
pos = endOfArray(ctofNums, i) and fillArray(array, i+1);
end

function endOfArray(a, b)
return a >= b;
end

function irEnd(array, modArray, i)
pos = irEndChecker(i+1) and irEnd(array, modArray, i+2);
modArray[i] = array[i];
print(modArray[i]);
end

function irEndChecker(a)
return a < ctofNums;
end

main();  


print(modArray[i]); выводит все в нужном мне порядке (9,7,5,3,1, и четные между ними),
только вот мне не выводить нужно а присваивать (получается 1,3,5,7,9 в нужном массиве).
Что мне сделать с рекурсивной функцией не только для корректного вывода, но и для конкретного
присваивания?
    


Ответы

Ответ 1



Для начала, приведу полный код решения задачи. Сможете убедиться, что эта мешанина скобок даёт нужный результат. Если после прочтения кода он останется непонятым, если явные отсылки к SICP останутся незамеченными, то прошу листать ниже -- там я попытаюсь объяснить, как работает этот оголтелый псевдолисп. -- Создание пары function cons(_x, _y) return function(m) return (m == 0 and _x) or _y end end -- Получаем "голову" списка (первый элемент) function head(list) return (list ~= nil and list(0)) or nil end -- Получаем "хвост" списка (всё кроме первого элемента) function tail(list) return (list ~= nil and list(1)) or nil end -- Собираем список в строку function list2str(list) return (tail(list) ~= nil and head(list) .. ', ' .. list2str(tail(list))) or head(list) end -- Собираем список из параметров функции function make_list(head, ...) return (... ~= nil and cons(head,make_list(...))) or cons(head, nil) end -- Переворачиваем список function reverse_list(list) return reverse_acc( list, nil ) end -- Сервисная функция с аккумулятором для переворота списка function reverse_acc(list, acc) return (tail(list) ~= nil and reverse_acc( tail(list), cons( head(list), acc ) ) ) or cons( head(list), acc ) end -- Получаем каждый второй элемент списка. От index зависит, -- будут ли это чётные или нечётные элементы function get_sublist(list, index) return (tail(list) ~= nil and (index == 1 and cons( head(list), get_sublist( tail(list), 0)) or get_sublist( tail(list), 1) ) ) or (index == 1 and cons(head(list), nil) ) or nil end -- Получаем нечётные елементы списка function get_odd(list) return get_sublist(list, 1) end -- Получаем чётные елементы списка function get_even(list) return get_sublist(list, 0) end -- Собираем в один два списка {a1, a2, ...} и {b1, b2, ...} -- по принципу: {a1, b1, a2, b2, ...} function megre_lists(list1, list2) return (tail(list1) ~= nil and cons( head(list1), cons( head(list2), megre_lists( tail(list1), tail(list2) )))) or cons( head(list1), head(list2) ~= nil and cons( head(list2),nil) or nil) end function main_task(list) -- Печать первоначального списка print( list2str(list) ) -- Тестирование функции переворачивания списка print( list2str(reverse_list(list)) ) -- Тестирование получения нечётных элементов print( list2str(get_odd(list)) ) -- Тестирование получения чётных элементов print( list2str(get_even(list)) ) -- Получаем список нечётных элементов, переворачиваем его и сливаем со списком -- чётных элементов print ( list2str( megre_lists( reverse_list( get_odd(list) ), get_even(list) ) ) ) end main_task( make_list(unpack( {1, 2, 3, 4, 5, 6, 7, 8, 9} )) ) Начинаем разбираться с принципами функционального программирования. В первую очередь, нам нужен истинно функциональный тип данных. То есть, данные мы будем хранить прямо в функции. И да, этот тип данных -- неизменяемый. function cons(_x, _y) return function(m) return (m == 0 and _x) or _y end end Функция возвращает функциональный тип данных, который будем называть «пара». Этот тип данных содержит два значения. Чтобы эти значения мы могли получать, нам нужно ввести ещё две функции: function head(list) return (list ~= nil and list(0)) or nil end function tail(list) return (list ~= nil and list(1)) or nil end Функция head (голова) позволяет получить первый элемент из пары, а функция tail (хвост) – второй элемент. Почему у функций названия «голова» и «хвост» станет понятно далее. Кстати, уже видно как мы будем обходиться без условного оператора. На самом деле, условный оператор нам нужен. Без него из рекурсии не выйти. Поэтому пользуемся удобным синтаксисом lua и совершенно не меняя логику работы вместо if condition then code1 else code2 end пишем так: condition and code1 or code2 С условным оператором разобрались. Продолжаем делать функциональные типы данных. Нам нужны списки для привычной функциональной парадигмы работы с данными. Для этого мы используем функциональную пару. В первый элемент пары мы записываем первый элемент списка, а во второй – вторую пару. Во второй паре у нас опять значение находится в первом элементе, а во втором – следующая пара. Получается своеобразная цепь из пар. Во втором элементе последней пары в цепи будем устанавливать nil. Это будет признаком конца списка. Функция make_list формирует список из своих аргументов. Вот теперь появляется смысл у названий функций head и tail. Первая функция возвращает первый элемент списка, а вторая – весь остальной список. Как голова удава, и его хвост. Если вы не знакомы с функциональным программированием, то просто поверьте на слово – трёх функций cons, head, tail вполне достаточно для любых работ с функциональными списками. А дальше мы решаем все задачи при помощи функций с рекурсиями. Это как нарисовать сову. Это как на лекции по математике лектор говорит: "отсюда легко видеть". Всё скучно и логично. Скучно для тех, кто знаком с базовыми алгоритмами функционального программирования. Остальных я отсылаю к SICP или любой другой книге по функциональному программированию.

воскресенье, 16 февраля 2020 г.

Как правильно поместить метод в таблицу?

#lua


Вот такой пример не работает, в нём при вызове функции self == nil:

function test()
  return {
    foo = "bar",
    say = function()
      print(self.foo)
    end
  }
end


А вот так всё работает, но такая запись мне не нравится ):

function test()
  local result = {foo = "bar"}
  function result:say()
    print(self.foo)
  end
  return result
end


Проверка:

local t = test()
t:say()

    


Ответы

Ответ 1



У функции должен быть параметр self: function test() return { foo = "bar", say = function(self) print(self.foo) end } end Вызовы вида t:say() на самом деле всегда разворачивается в t.say(t) и если вы хотите внутри функции say() иметь доступ к полям таблицы, вам нужно объявить как минимум один параметр (с любым именем), в который вам и будет передана эта таблица. В Lua, общепринятое имя для такого параметра - self, но вы можете использовать любое. Если у функции есть несколько параметров, то таблица будет всегда передаваться в качестве "нулевого" параметра, т.е вызов t:say(x, y) будет транслироваться в t.say(t, x, y) и при объявлении функции вы всегда должны ожидать параметр self на первом месте: say = function(self, x, y) print(self.foo) end Что касается синтаксиса с двоеточием, то это т.н. синтаксический сахар. В Lua на самом деле нету классов, и их можно лишь эмулировать (в некоторой степени) при помощи таблиц. И чтобы эта эмуляция выглядела похожей, на использование классов, и был введён такой синтаксис.

Lua, функциональное программирование

#массивы #алгоритм #функции #lua


Дана задача: в массиве чисел (допустим 1 2 3 4 5 6 7 8 9) реверсировать числа на
нечетных позициях (тут это 1,3,5,7,9, должно получится 9 2 7 4 5 6 3 8 1).
Все это сделать с помощью функционального программирования:
Запрещено использовать циклы, условные операторы, операторы присваивания, операторы
контроля (return можно). Все делать с помощью рекурсий и вызовов других функций.

Вот что я набросал:  

local function main()
array = {};
modArray = {};

--Finding count of nums
file = io.open("nums.txt", "r");
numsArrayString = file:read();

stofSpaces = string.gsub(numsArrayString, "%d", "")
ctofNums = string.len(stofSpaces);
file:close();

--Filling array
file = io.open("nums.txt", "r");
fillArray(array, 0);
file:close();

--1st part
irEnd(array, modArray, 0);
end

function fillArray(array, i)
array[i] = file:read("*n");
pos = endOfArray(ctofNums, i) and fillArray(array, i+1);
end

function endOfArray(a, b)
return a >= b;
end

function irEnd(array, modArray, i)
pos = irEndChecker(i+1) and irEnd(array, modArray, i+2);
modArray[i] = array[i];
print(modArray[i]);
end

function irEndChecker(a)
return a < ctofNums;
end

main();  


print(modArray[i]); выводит все в нужном мне порядке (9,7,5,3,1, и четные между ними),
только вот мне не выводить нужно а присваивать (получается 1,3,5,7,9 в нужном массиве).
Что мне сделать с рекурсивной функцией не только для корректного вывода, но и для конкретного
присваивания?
    


Ответы

Ответ 1



Для начала, приведу полный код решения задачи. Сможете убедиться, что эта мешанина скобок даёт нужный результат. Если после прочтения кода он останется непонятым, если явные отсылки к SICP останутся незамеченными, то прошу листать ниже -- там я попытаюсь объяснить, как работает этот оголтелый псевдолисп. -- Создание пары function cons(_x, _y) return function(m) return (m == 0 and _x) or _y end end -- Получаем "голову" списка (первый элемент) function head(list) return (list ~= nil and list(0)) or nil end -- Получаем "хвост" списка (всё кроме первого элемента) function tail(list) return (list ~= nil and list(1)) or nil end -- Собираем список в строку function list2str(list) return (tail(list) ~= nil and head(list) .. ', ' .. list2str(tail(list))) or head(list) end -- Собираем список из параметров функции function make_list(head, ...) return (... ~= nil and cons(head,make_list(...))) or cons(head, nil) end -- Переворачиваем список function reverse_list(list) return reverse_acc( list, nil ) end -- Сервисная функция с аккумулятором для переворота списка function reverse_acc(list, acc) return (tail(list) ~= nil and reverse_acc( tail(list), cons( head(list), acc ) ) ) or cons( head(list), acc ) end -- Получаем каждый второй элемент списка. От index зависит, -- будут ли это чётные или нечётные элементы function get_sublist(list, index) return (tail(list) ~= nil and (index == 1 and cons( head(list), get_sublist( tail(list), 0)) or get_sublist( tail(list), 1) ) ) or (index == 1 and cons(head(list), nil) ) or nil end -- Получаем нечётные елементы списка function get_odd(list) return get_sublist(list, 1) end -- Получаем чётные елементы списка function get_even(list) return get_sublist(list, 0) end -- Собираем в один два списка {a1, a2, ...} и {b1, b2, ...} -- по принципу: {a1, b1, a2, b2, ...} function megre_lists(list1, list2) return (tail(list1) ~= nil and cons( head(list1), cons( head(list2), megre_lists( tail(list1), tail(list2) )))) or cons( head(list1), head(list2) ~= nil and cons( head(list2),nil) or nil) end function main_task(list) -- Печать первоначального списка print( list2str(list) ) -- Тестирование функции переворачивания списка print( list2str(reverse_list(list)) ) -- Тестирование получения нечётных элементов print( list2str(get_odd(list)) ) -- Тестирование получения чётных элементов print( list2str(get_even(list)) ) -- Получаем список нечётных элементов, переворачиваем его и сливаем со списком -- чётных элементов print ( list2str( megre_lists( reverse_list( get_odd(list) ), get_even(list) ) ) ) end main_task( make_list(unpack( {1, 2, 3, 4, 5, 6, 7, 8, 9} )) ) Начинаем разбираться с принципами функционального программирования. В первую очередь, нам нужен истинно функциональный тип данных. То есть, данные мы будем хранить прямо в функции. И да, этот тип данных -- неизменяемый. function cons(_x, _y) return function(m) return (m == 0 and _x) or _y end end Функция возвращает функциональный тип данных, который будем называть «пара». Этот тип данных содержит два значения. Чтобы эти значения мы могли получать, нам нужно ввести ещё две функции: function head(list) return (list ~= nil and list(0)) or nil end function tail(list) return (list ~= nil and list(1)) or nil end Функция head (голова) позволяет получить первый элемент из пары, а функция tail (хвост) – второй элемент. Почему у функций названия «голова» и «хвост» станет понятно далее. Кстати, уже видно как мы будем обходиться без условного оператора. На самом деле, условный оператор нам нужен. Без него из рекурсии не выйти. Поэтому пользуемся удобным синтаксисом lua и совершенно не меняя логику работы вместо if condition then code1 else code2 end пишем так: condition and code1 or code2 С условным оператором разобрались. Продолжаем делать функциональные типы данных. Нам нужны списки для привычной функциональной парадигмы работы с данными. Для этого мы используем функциональную пару. В первый элемент пары мы записываем первый элемент списка, а во второй – вторую пару. Во второй паре у нас опять значение находится в первом элементе, а во втором – следующая пара. Получается своеобразная цепь из пар. Во втором элементе последней пары в цепи будем устанавливать nil. Это будет признаком конца списка. Функция make_list формирует список из своих аргументов. Вот теперь появляется смысл у названий функций head и tail. Первая функция возвращает первый элемент списка, а вторая – весь остальной список. Как голова удава, и его хвост. Если вы не знакомы с функциональным программированием, то просто поверьте на слово – трёх функций cons, head, tail вполне достаточно для любых работ с функциональными списками. А дальше мы решаем все задачи при помощи функций с рекурсиями. Это как нарисовать сову. Это как на лекции по математике лектор говорит: "отсюда легко видеть". Всё скучно и логично. Скучно для тех, кто знаком с базовыми алгоритмами функционального программирования. Остальных я отсылаю к SICP или любой другой книге по функциональному программированию.

пятница, 24 января 2020 г.

Подключение к postgresql из lapis

#lua


Добрый день, решил поиграться с lapis - https://github.com/leafo/lapis, но приложение
падает при попытке запроса к базе(PostgreSQL) с выводом:

2017/07/01 16:04:26 [error] 31284#0: *8 lua entry thread aborted: runtime error:
attempt to yield across C-call boundary stack traceback: coroutine 0:
        [C]: in function 'require'
        /usr/local/share/lua/5.1/lapis/init.lua:15: in function 'serve'
        content_by_lua(nginx.conf.compiled:22):2: in function ,
client: 127.0.0.1, server: , request: "GET / HTTP/1.1", host: "localhost:8080"


Код вызывающий ошибку

local db = require("lapis.db")
local res = db.query("SELECT * FROM users");


config.lua

config({ "development", "production" }, {
    postgres = {
        host = "0.0.0.0",
        port = "5432",
        user = "wars_base",
        password = "12345",
        database = "wars_base"
    }
})


База работает, таблица создана, в таблице 1 запись.

В чем может быть проблема?
    


Ответы

Ответ 1



Ответ: Выполнять запросы к БД возможно только в обработчиках запросов к серверу. https://github.com/leafo/lapis/issues/556

суббота, 4 января 2020 г.

Поведение таблиц в Lua при присваивании

#lua


Я новичок в lua, и для меня не очевидно такое поведение:

local a = {
  name = "A",
  counter = 0
}

function a:say ()
  self.counter = self.counter + 1
  print(self.name..": "..self.counter)
end

local b = a
b.name = "B"

a:say() -- результат B: 1 -- ожидалось A: 1
a:say() -- результат B: 2 -- ожидалось A: 2
a:say() -- результат B: 3 -- ожидалось A: 3
b:say() -- результат B: 4 -- ожидалось B: 1


Как мне скопировать новый экземпляр таблицы, а не наследовать от старой?
    


Ответы

Ответ 1



В луа таблицы при присваивании не копируются,a и b - cсылки на таблицу, одну и ту же. Чтоб создать новую таблицу вам надо реализовать функцию копирования и использовать ее: function table.copy (originalTable) local copyTable = {} for k,v in pairs(originalTable) do copyTable[k] = v end return copyTable end local b = table.copy(a)

вторник, 31 декабря 2019 г.

Создание массивов из текста

#php #регулярные_выражения #lua #match


У меня например есть строка

s = "Hello, world! {255,0,0}Привет, мир! {155,55,0}How are {100,3,5you?"


Мне нужно разделить с помощью выражений, а именно функцией string.gmatch, на массив
который будет вмещать в себе масивы с цвета R, G, B и самой строки. То есть чтобы в
результате я получил массив:

arr[1] = {0, 0, 0, "Hello, world! "}
// Из-за того что в начале строки нет кода цвета, то по умолчанию ставить цвет по нулям

arr[2] = {255, 0, 0, "Привет, мир! "}

arr[3] = {155, 55, 0, "How are {100,3,5you?"}
// "How are {100,3,5you?" - именно так, по скольку неправильно сформирован код цвета,
также если будет например буква внутри {100,3Я,5}, то это также все пойдет в строку


Вот что у меня получилось написать на LUA:

arr = {}
s = "Hello, world! {255,0,0}Привет, мир! {155,55,0}How are {100,3,5you?"
for k, v in string.gmatch(s, "{(%d+,%d+,%d+)}([^{]*)") do
    local r, g, b = string.match(k, "(%d+),(%d+),(%d+)")
    table.insert(arr, {r, g, b, v})
end


В результате получаю массив:

arr[1] = {255, 0, 0, "Привет, мир! "}
arr[2] = {155, 55, 0, "How are "}


Но меня это не устраивает:

1) нет части строки, в которой не указан цвет, то есть нет массива 
arr[1] = {0, 0, 0, "Hello, world! "}

2) Обрезает до следующего символа {, а нужно чтобы обрезало до комбинации цвета

Как по-другому это реализовать (но в простом варианте, то есть так, чтобы не было
большой нагрузки), или хотя бы как например такое реализовать в PHP, а я бы попытался
уже как-то перевести на LUA
    


Ответы

Ответ 1



Может кто-то знает как по другому это реализовать (но в простом варианте, тоесть так чтобы не было большой нагрузки), или хотя бы как например такое реализовать в PHP Вот такая реализация: $s = "Hello, world! {255,0,0}Привет, мир! {155,55,0}How are {100,3,5you?"; $res = []; // Делим предложение $words = explode(" {", $s); foreach($words as $k => $w){ // цвета R, G, B preg_match('/(\d+,\d+,\d+)\}?/', $w, $match); if(!empty($match)){ // Только текст без цветов R, G, B preg_match('/[^(\d+,\d+,\d+)\}?].+/', $w, $not_m); $res[] = [$match[1], $not_m[0]]; }else{ $res[] = ['0,0,0', $w]; } } print_r($res); Получаем результат: Array ( [0] => Array ( [0] => 0,0,0 [1] => Hello, world! ) [1] => Array ( [0] => 255,0,0 [1] => Привет, мир! ) [2] => Array ( [0] => 155,55,0 [1] => How are ) [3] => Array ( [0] => 100,3,5 [1] => you? ) )

Ответ 2



// TEXT $text = "Hello, world! {255,0,0}Привет, мир! {155,55,0}How are {100,3,5you?"; // PARSE preg_match_all("#{(\d+,\d+,\d+)}#Uu", $text, $matches, PREG_OFFSET_CAPTURE|PREG_SET_ORDER); // VAR $result = []; $color = '0,0,0'; $offset = 0; // EACH foreach ($matches as $key => $value) { $text_sub = substr($text, $offset, ($value[0][1] - $offset)); if (mb_strlen($text_sub) > 0) { $result[] = array_merge(explode(',', $color), [$text_sub]); } $offset = $value[0][1] + mb_strlen($value[0][0]); $color = $value[1][0]; } // LAST $text_sub = substr($text, $offset); if (mb_strlen($text_sub) > 0) { $result[] = array_merge(explode(',', $color), [$text_sub]); } var_dump($result); Может и костыльно, но то что вам нужно, делает.

Ответ 3



Вот вам вариант без циклов: $text = '{100,0,0}Hello, world! {255,0,0}Привет, мир! {155,55,0}How are {100,3,5you?'; $str = array_filter(preg_split('~{(\d+),(\d+),(\d+)}~', $text)); preg_match_all('~(.+?){~', $text, $matches, PREG_SET_ORDER); $result = array_map(function ($x, $y) { return (!preg_match_all('/(\d+),(\d+),(\d+)/', $x[0], $match)) ? [0, 0, 0, $y] : [$match[1][0], $match[2][0], $match[3][0], $y]; }, $matches, $str); print_r($result);

Ответ 4



Вот примерно так получилось: s = "Hello, world! {255,0,0}Привет, мир! {155,55,0}How are {100,3,5you?" s = s .. "\n" -- вынужденная мера, обозначим конец строки arr = {} local arr = {{0,0,0,""}} local i=1 for case in s:gmatch ("(.-[%c}])") do -- print ( case ) local text = case:match("(.+){") local r, g, b = case:match( "{(%d+),(%d+),(%d+)}") if r and g and b then arr[i+1] = {r,g,b,""} else text = case end arr[i][4] = text i=i+1 end -- test for k,v in pairs(arr) do print(k, v[1], v[2], v[3], v[4]) end результат: 1 0 0 0 Hello, world! 2 255 0 0 Привет, мир! 3 155 55 0 How are {100,3,5you?

понедельник, 16 декабря 2019 г.

Прокрутка меню в Corona SDK

#lua #menu #coronasdk


Создаю прокрутку для меню в Lua CORONA SDK.

Исходник файла menu.lua

local composer = require( "composer" )
local widget = require( "widget" )

local scene = composer.newScene()

local function onButtonRelease( event )
composer.gotoScene( event.target.id:lower(), { effect="fade", time=300 } )
--composer.recycleOnSceneChange = true
end

function scene:create( event )

local sceneGroup = self.view

local sceneTitle = display.newText( sceneGroup, "Выберете уровень", display.contentCenterX,
10, composer.getVariable( "appFont" ), 20 )

-- Создание массива из кнопок меню
local menuButtons = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
"13", "14", "15", "16", "17", "18"  }

-- Создание цикла для кнопок меню
local rowNum = 0
for i = 1,#menuButtons do

    rowNum = rowNum+1
    local button = widget.newButton(
        {
            label = menuButtons[i],
            id = menuButtons[i],
            shape = "circle",
            radius = 20,
            font = composer.getVariable( "appFont" ), 
            fontSize = 16,
            fillColor = { default={ 0.12,0.32,0.52,1 } ,over={ 0.132,0.352,0.572,1
} }, -- цвет кнопки и нажатия на кнопку
            labelColor = { default={ 1,1,1,1 }, over={ 1,1,1,1 } }, -- цвет шрифта
на кнопках
            onRelease = onButtonRelease
        })

   mod = math.fmod(i, 2) -- определение кратности

    if (i*mod >= 1 ) then -- если кратно 1
        button.x = display.contentCenterX -50
        elseif (i*mod == 0) then  -- если кратно 0 
            button.x = display.contentCenterX + 50 
        end     
    button.y = 65 + ((rowNum-1)*35) -- растояние по y между кнопками

    sceneGroup:insert( button ) -- обновление сцены после нажатия на кнопку
end

end

scene:addEventListener( "create", scene ) -- слушатель на создание сцены


--обработка касаний
function scene:touch(e)
-- body
if(e.phase == "began") then
        print("начало a");
    elseif (e.phase == "moved") then
        scene.x = e.x;
        scene.y = e.y;
        print("двигаю a");
    elseif(e.phase == "ended") then
        print("отпустил a");
end
end

 scene:addEventListener("touch", scene);

return scene


Для наглядности прикрепляю изображение



По обработке касаний, пробовал как со сценой, так и с меню, и с массивом кнопок.

Подскажите, пожалуйста, решение моей проблемы.
    


Ответы

Ответ 1



Для выполнения поставленной цели, нужно создать scrollView и переместить в функцию код на создания кнопок в цикле из function scene:create в function showSlidingMenu и слушать события каждый раз в Runtime. Для реализации прокрутки меню сразу при переходе в меню, функция вызывается в scene:create local composer = require( "composer" ) local widget = require( "widget" ) local scrollView local scene = composer.newScene() local function onButtonRelease( event ) composer.gotoScene( event.target.id:lower(), { effect="fade", time=300 } ) end function scene:create( event ) sceneGroup = self.view composer.recycleOnSceneChange = true; local sceneTitle = display.newText( sceneGroup, "Выберете уровень", display.contentCenterX, 10, composer.getVariable( "appFont" ), 20 ) end local function ButtonListener( event ) if ( event.phase == "moved" ) then local dx = math.abs( event.x - event.xStart ) if ( dx > 5 ) then scrollView:takeFocus( event ) end elseif ( event.phase == "ended" ) then print( "клик по объекту" ) timer.performWithDelay( 10, function() scrollView:removeSelf(); scrollView = nil; Runtime:removeEventListener("touch",showSlidingMenu) end ) end return true end function showSlidingMenu( event ) if ( "ended" == event.phase ) then scrollView = widget.newScrollView { width = _W, height = _H, scrollWidth = width, scrollHeight = height, horizontalScrollDisabled = true } scrollView.x = display.contentCenterX scrollView.y = display.contentCenterY local menuButtons = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18" } rowNum = 0 for i = 1,#menuButtons do rowNum = rowNum+1 local button = widget.newButton( { label = menuButtons[i], id = menuButtons[i], shape = "circle", radius = 20, font = composer.getVariable( "appFont" ), fontSize = 16, fillColor = { default={ 0.12,0.32,0.52,1 } ,over={ 0.132,0.352,0.572,1 } }, -- цвет кнопки и нажатия на кнопку labelColor = { default={ 1,1,1,1 }, over={ 1,1,1,1 } }, -- цвет шрифта на кнопках onRelease = onButtonRelease }) mod = math.fmod(i, 2) -- определение кратности if (i*mod >= 1 ) then -- если кратно 1 button.x = display.contentCenterX -50 -- смещение относительно центра влево elseif (i*mod == 0) then -- если кратно 0 button.x = display.contentCenterX + 50 -- смещение относительно центра впрово end button.y = 30 + ((rowNum-1)*35) -- координаты первой кнопки и растояние по y между кнопками scrollView:insert( button ) sceneGroup:insert( scrollView ) scene:addEventListener( "touch", ButtonListener ) end return true end end -- hide() function scene:hide( event ) local sceneGroup = self.view local phase = event.phase if ( phase == "will" ) then -- Код здесь работает, когда сцена находится на экране (но собирается уйти с экрана) print ("ухожу с экрана"); elseif ( phase == "did" ) then -- Код здесь запускается сразу после того, как сцена полностью выходит за пределы экрана print ("ушел за пределы экрана"); end end -- destroy() function scene:destroy( event ) local sceneGroup = self.view showSlidingMenu = nil; end scene:addEventListener( "create", scene ) -- слушатель на создание сцены scene:addEventListener( "destroy", scene ) scene:addEventListener( "hide", scene ) Runtime:addEventListener("touch",showSlidingMenu) return scene Если тема создания меню с помощью Corona SDK интересна, то буду ее развивать

среда, 11 декабря 2019 г.

Как правильно растеризовать окружность без повторов и нарушения симметрии?

#алгоритм #lua


Я пытаюсь создать круг из объектов (ящиков) для мода из одной игры, используя этот код:

function circle(x,y,r)
local angle = 0

x = x + 0.5 - (x + 0.5) % 1
y = y + 0.5 - (x + 0.5) % 1
r =  r + 0.5 прибавляю 0.5 чтобы объект появлялся в центре клетки
    while angle < 2*math.pi do
    angle = angle + r/r^2

    local xs = (x  +   r * math.cos (angle))
    local ys = (y  +   r * math.sin (angle))
game.surfaces[1].create_entity{name = "wooden-chest", position= {xs,ys},force='player'}
--создание объекта с указанными координатами
end end 


Объекты могут размещаться только в середине клетки. то есть если координаты например
будут х=0.9 у=0.9, то объект разместиться на координатах х=0.5 у=0.5
Также и с отрицательными координатами х=-0.9 у=-0.9 ==> х=-0.5 у=-0.5.
Создаем окружность: circle(0,0,15)



Голубые стрелки указывают на то что обьект в этих местах разместился повторно(видна
двойная тень)
Оранжевым прямоугольником я выделил несоответствия.
При радиусе в 10 с половиной клеток круг становится ровным, но повторное размещение
в одной и той же клетке присутствует:



Что нужно поправить в этой функции, чтобы окружность правильно проходила через середины
клеток? Возможно мне бы помог Алгоритм Брезенхе́ма или алгоритм окружности Midpoint,
но я не знаю как написать их на lua, так как еще довольно слаб в программировании. 
UPD: Я попробовал преобразовать этот код на C++ в Lua:

public static void BresenhamCircle(Graphics g, Color clr, int _x, int _y, int radius)
    {
        int x = 0, y = radius, gap = 0, delta = (2 - 2 * radius);
        while (y >= 0)
        {
            PutPixel(g, clr, _x + x, _y + y, 255);
            PutPixel(g, clr, _x + x, _y - y, 255);
            PutPixel(g, clr, _x - x, _y - y, 255);
            PutPixel(g, clr, _x - x, _y + y, 255);
            gap = 2 * (delta + y) - 1;
            if (delta < 0 && gap <= 0)
            {
                x++;
                delta += 2 * x + 1;
                continue;
            }
            if (delta > 0 && gap > 0)
            {
                y--;
                delta -= 2 * y + 1;
                continue;
            }
            x++;
            delta += 2 * (x - y);
            y--;
        }
    }
function bresenham_circle(X1,Y1,R)

       local  x = 0 local y = R local gap = 0 local delta = (2 - 2 * R)
        while (y >= 0) do 

        game.surfaces[1].create_entity{name = "wooden-chest", position= {X1 + x,
Y1 + y},force='player'}
        game.surfaces[1].create_entity{name = "wooden-chest", position= {X1 + x,
Y1 - y},force='player'}
        game.surfaces[1].create_entity{name = "wooden-chest", position= {X1 - x,
Y1 + y},force='player'}
        game.surfaces[1].create_entity{name = "wooden-chest", position= {X1 - x,
Y1 - y},force='player'}
            gap = 2 * (delta + y) - 1
            if (delta >= 0 and gap > 0) then 
            if (delta <= 0 and gap <= 0) then 
            y = y - 1
            x =x+1
            delta = delta +2 *(x-y)
            else y=y - 1  delta = delta-2*y+1
            end
            else     x=x+1 delta = delta+ 2 * x + 1
            end

        end
        end


Но я получаю Овал, на одну клетку смещенный от центра по обоим осям, а также имеющий
дубль-размещение в 4 местах
UPD2: Смещение решилось добавлением 0.5 к x и y

Сейчас мой код выглядит так:

function bresenham_circle(X1,Y1,R)
local R = R+0.5
local  x = 0.25 
local y = R 
local gap = 0 
local delta = (2 - 2 * R)
while (y >= 0) do 

    game.surfaces[1].create_entity{name = "wooden-chest", position= {X1 + x, Y1 -
y},force='player'}
    game.surfaces[1].create_entity{name = "wooden-chest", position= {X1 - x, Y1 -
y},force='player'}
    game.surfaces[1].create_entity{name = "wooden-chest", position= {X1 - x, Y1 +
y},force='player'}
    game.surfaces[1].create_entity{name = "wooden-chest", position= {X1 + x, Y1 +
y},force='player'}        

    gap = 2 * (delta + y) - 1
    if (delta >= 0 and gap > 0) then 
        if (delta <= 0 and gap <= 0) then 
            y = y - 1
            x =x+1
            delta = delta +2 *((x)-(y))
            else   delta = delta-2*(y-1)+1 y=y - 1
        end
        else      delta = delta+ 2 * (x+1) + 1 x=x+1
    end
end end


Повторов нет с любым радиусом, но все еще нет симметрии. Любые подсказки?

    


Ответы

Ответ 1



Вот код алгоритма Брезенхема, написанный при помощи PascalABC, и работающий по "восьмушкам" - внутри каждого квадранта точки ставятся в направлении от краев квадранта вовнутрь. Изначально был недостаток - повторно ставятся точки на позициях 0,pi/2,pi,2pi/3. Это можно поправить сравнением величин (y-y1)+y1 и -(y-y1)+y1, (x-x1)+x1 и -(x-x1)+x1. Если среди них есть хоть одно равенство (от смены знака "дельты" (то есть (y-y1) или (x-x1)) не изменилась координата), то не ставить точку. Тут не много специфичных команд, думаю, будет несложно переписать на нужный Вам язык. procedure brez; var x,y,x1,y1,r:integer; e:real; begin x1:=250; y1:=250; r:=200; x:=x1; y:=y1+r; e:=3-2*(r); while (x-x1)<(y-y1) do begin putpixel((x-x1)+x1,(y-y1)+y1,clRed); putpixel((y-y1)+y1,-(x-x1)+x1,clRed); putpixel(-(x-x1)+x1,-(y-y1)+y1,clRed); putpixel(-(y-y1)+y1,(x-x1)+x1,clRed); if not( ( ((x-x1)+x1) = (-(x-x1)+x1) ) or ( ((y-y1)+y1)=(-(y-y1)+y1) ) ) then begin putpixel((y-y1)+y1,(x-x1)+x1,clRed); putpixel((x-x1)+x1,-(y-y1)+y1,clRed); putpixel(-(y-y1)+y1,-(x-x1)+x1,clRed); putpixel(-(x-x1)+x1,(y-y1)+y1,clRed) end; if e<0 then e:=e+4*(x-x1)+6 else begin e:=e+4*(x-x1-y+y1)+10; y:=y-1 end; x:=x+1 end; if x=y then begin putpixel((x-x1)+x1,(y-y1)+y1,clRed); putpixel((y-y1)+y1,(x-x1)+x1,clRed); putpixel((y-y1)+y1,-(x-x1)+x1,clRed); putpixel((x-x1)+x1,-(y-y1)+y1,clRed); putpixel(-(x-x1)+x1,-(y-y1)+y1,clRed); putpixel(-(y-y1)+y1,-(x-x1)+x1,clRed); putpixel(-(y-y1)+y1,(x-x1)+x1,clRed); putpixel(-(x-x1)+x1,(y-y1)+y1,clRed); end end;

суббота, 7 декабря 2019 г.

Работа с stdin и stdout дочернего процесса одновременно в lua

#linux #lua #процесс #io


При написании lua скрипта наткнулся на одну вещь: io.popen, судя по документации
не может передать одновременно дескрипторы stdin и stdout запускаемого процесса.

Пример проблемного кода:

cmdexecute = function(cmd, input)
    local f = io.popen(cmd, "w");
    if tostring(input) ~= nil then
        f:write(tostring(input));
    end;
    local l = f:read("*a");
    f:close();
    return (l);
end;

if cmdexecute('xargs ps', 'aux') ~= nil then
    print 'ps aux что-то выдал';
else
    print 'ps aux промолчал';
end;


Результат выполнения:

Длинный-длинный вывод ps aux...
ps aux промолчал


А должно быть по идее:

ps aux что-то выдал


Вопрос: как получить дескрипторы и stdin, и stdout из popen? Или, возможно, есть
какое-то другое решение задачи, без io.popen?

Upd: по причине актуальности вопроса поясняю: решения с ffi допустимы.
    


Ответы

Ответ 1



Вам должен помочь модуль lua-subprocess Функция subprocess.popen принимает последним аргументом таблицу с именованными опциями. Среди них есть опции stdin, stdout и stderr, куда можно передать строку (имя файла), число (системный дескриптор файла), файловый объект или специальную константу subprocess.PIPE, которая создаст пайп для этого потока и сохранит его в возвращаемом объекте.

Ответ 2



io.popen дает интерфейс к системной posix функции popen, да, она может работать только в одну сторону. В вашем случае писать что то в ps не нужно. Если открыть ее для чтения - она конечно прочитает. Но я так понимаю ps у вас только для примера ... В POSIX для работы с обоими файловыми дескрипторами вызываемого процесса есть только 1 путь. Создание пар сокетов, привязка одного из концов этих соединений к файловым дескрипторам 0 и 1 (dup2). После чего выполняется fork и дочерний процесс вызывает нужную программу. В Lua судя по документации подобных средств не предусмотрено. Так что прямой ответ на ваш вопрос - получить и stdin и stdout одновременно невозможно. Из решений вижу только перенаправление вывода программы в файл и последующее чтение этого файла. Или ввод для программы пишется заранее в файл и подается на вход. И можно еще поэкспериментировать с именованными каналами. mkfifo создать именованный канал в файловой системе направить его в программу или запись из программы перенаправить в него и в lua открыть этот канал как обычный файл и попробовать писать/читать.

четверг, 5 декабря 2019 г.

Логирование lua в syslog

#linux #логирование #lua


Как напрямую писать логи с Openresty Lua в syslog?

При установке библиотеки luasyslog возникает ошибка:

ng-gateway_1  | error loading module 'vendors.lsyslog' from file '/usr/local/openresty/site/lualib/vendors/lsyslog.so':
ng-gateway_1  |     /usr/local/openresty/site/lualib/vendors/lsyslog.so: undefined
symbol: luaopen_vendors_lsyslog


Подскажите как решить проблему, или какую библиотеку использовать.
Спасибо!
    


Ответы

Ответ 1



Было принято решение писать в syslog сервера на уровне nginx. В docker-compose в volumes добавил bind на локальный /dev/log: - type: bind source: /dev/log target: /dev/log В nginx.conf, директиве server прокинул 2 unix socket-а: # Logging configurations error_log syslog:server=unix:/dev/log,tag=ng_trace; access_log syslog:server=unix:/dev/log,tag=ng_trace,nohostname; Для логирования в lua использую ngx.log(ngx.ERR, 'Log message') Все логи из докер-контейнера пишутся прямиком в syslog сервера.

Ответ 2



Openresty не пользовал, поэтому за правильность ответа - не ручаюсь. По приведенным логам ситуация похожа на не согласование версий. Возможно отрывок из FAQ Openresty ситуацию прояснит: ** Why can't I use Lua 5.2 or later? ** Lua 5.2+ are incompatible with Lua 5.1 on both the C API land and the Lua land (including various language semantics). If as you said there are quite a few people already using ngx_lua + Lua 5.1, then linking against Lua 5.2+ will probably break these people's existing Lua code. Lua 5.2+ are essentially incompatible different languages. Supporting Lua 5.2+ requires nontrivial architectural changes in ngx_lua's basic infrastructure. The most troublesome thing is the quite different "environments" model in Lua 5.2+. At this point, we would hold back adding support for Lua 5.2+ to ngx_lua. Also, we do not want to create confusions and incompatibilities on the Lua land for applications running atop ngx_lua, as well as all the existing lua-resty-* libraries written in the Lua 5.1 language. We believe it is better to stick with one Lua language in ngx_lua. Chasing the Lua language's version number has not many practical technical merits (if there were some political ones). Резюме: смотрите версию используемого Lua

среда, 19 июня 2019 г.

Библиотека gettext в lua

Необходима библиотека gettext для lua (CGI-сайт). Какие существуют решения?


Ответ

Решение на ffi: https://github.com/bungle/lua-resty-gettext. Данная реализация просто пробрасывает все библиотечные функции gettext напрямую.

Как получить то, что было задано с define в lua FFI?

Многие функции системного программирования в linux работают с флагами (например, O_NONBLOCK). Но эти флаги же надо ещё и получить! Насколько я понимаю, флаги в заголовочных файлах определены при помощи #define. Традиционное нечто вроде ffi.C.O_NONBLOCK выдаёт ошибку, это не функция и не глобальная переменная! При этом файл заголовков подгружен при помощи библиотеки lcpp. И как же получить эти самые define'ы?


Ответ

Надо использовать не ffi.C. а ffi.lcpp_defs..
Пример 1
Файл test1.lua:
local lcpp = require("lcpp") local ffi = require("ffi")
ffi.cdef([[ #define MAX_SIZE 100 typedef struct { int data[MAX_SIZE]; } test_t; ]])
print("MAX_SIZE = " .. ffi.lcpp_defs.MAX_SIZE)
Результат работы:
MAX_SIZE = 100
Пример 2
Файл test2.lua:
local lcpp = require("lcpp") local ffi = require("ffi")
ffi.cdef("#include \"test.h\"")
print("FREQUENCY_MHZ = " .. ffi.lcpp_defs.FREQUENCY_MHZ) print("FREQUENCY_KHZ = " .. ffi.lcpp_defs.FREQUENCY_KHZ) print("_PACKED_ = " .. ffi.lcpp_defs._PACKED_)
Файл test.h:
#ifndef TEST_H #define TEST_H
#define FREQUENCY_MHZ 2 #define FREQUENCY_KHZ (FREQUENCY_MHZ * 1000)
#define _PACKED_ __attribute__((packed))
#endif // TEST_H
Результат работы:
FREQUENCY_MHZ = 2 FREQUENCY_KHZ = (FREQUENCY_MHZ * 1000) _PACKED_ = __attribute__((packed))
Всё в общем-то работает, но из второго примера видно, что полного препроцессинга define'ов не происходит. На самом деле тут всё не так плохо.
Пример 3
Файл test3.lua:
local ffi = require("ffi") local lcpp = require("lcpp")
local data, state = lcpp.compile([[ #define ROW_CNT 0x0F #define COL_CNT 0x0A #define DATA_CNT (ROW_CNT * COL_CNT) int data[DATA_CNT]; ]]) local data_type = ffi.typeof(data)
print("data = " .. data) print("ROW_CNT = " .. state.defines.ROW_CNT) print("COL_CNT = " .. state.defines.COL_CNT) print("DATA_CNT = " .. state.defines.DATA_CNT) print("typeof(data) = " .. tostring(data_type))
Результат работы:
data = int data[(15*10)]; ROW_CNT = 0x0F COL_CNT = 0x0A DATA_CNT = (ROW_CNT * COL_CNT) typeof(data) = ctype
Как видно из этого примера, размерность массива вычислена правильно (150 элементов).

понедельник, 17 июня 2019 г.

Использование _G и _ENV

Я знаю: _G - окружение единое для всех модулей, файлов, блоков кода и т.д., _ENV - частное для каждого.
Увы, нюансы их использования я не совсем понял. (То ли я дурак, то ли книга не очень)
_ENV уникально только для каждого модуля\файла или для каждого блока, будь то функция, цикл, do/end и т.д. ? По умолчанию _ENV присваивается _G? Доступны ли переменные local в _ENV?


Ответ

1 и 2: судя по luac -l -l, _ENV есть upvalue (т.е. забита для каждой функции, менять никто не запрещает), и да, по умолчанию она равна _G. По факту, как ясно из того же luac -l -l, весь доступ к "глобальным переменным" (точнее, свободным именам), идёт через _ENV 3. Нет, не доступны. Вы же их не видете в _G, они на то и локальные. UPD: что важно, это всё начинается в lua 5.2, в 5.1 такого нет.
P.S.: как раз моё по теме: http://goo.gl/E3d5Zb

воскресенье, 26 мая 2019 г.

Как правильно поместить метод в таблицу?

Вот такой пример не работает, в нём при вызове функции self == nil:
function test() return { foo = "bar", say = function() print(self.foo) end } end
А вот так всё работает, но такая запись мне не нравится ):
function test() local result = {foo = "bar"} function result:say() print(self.foo) end return result end
Проверка:
local t = test() t:say()


Ответ

У функции должен быть параметр self
function test() return { foo = "bar", say = function(self) print(self.foo) end } end
Вызовы вида t:say() на самом деле всегда разворачивается в t.say(t) и если вы хотите внутри функции say() иметь доступ к полям таблицы, вам нужно объявить как минимум один параметр (с любым именем), в который вам и будет передана эта таблица. В Lua, общепринятое имя для такого параметра - self, но вы можете использовать любое.
Если у функции есть несколько параметров, то таблица будет всегда передаваться в качестве "нулевого" параметра, т.е вызов t:say(x, y) будет транслироваться в t.say(t, x, y) и при объявлении функции вы всегда должны ожидать параметр self на первом месте:
say = function(self, x, y) print(self.foo) end
Что касается синтаксиса с двоеточием, то это т.н. синтаксический сахар. В Lua на самом деле нету классов, и их можно лишь эмулировать (в некоторой степени) при помощи таблиц. И чтобы эта эмуляция выглядела похожей, на использование классов, и был введён такой синтаксис.

пятница, 26 апреля 2019 г.

Поведение таблиц в Lua при присваивании

Я новичок в lua, и для меня не очевидно такое поведение:
local a = { name = "A", counter = 0 }
function a:say () self.counter = self.counter + 1 print(self.name..": "..self.counter) end
local b = a b.name = "B"
a:say() -- результат B: 1 -- ожидалось A: 1 a:say() -- результат B: 2 -- ожидалось A: 2 a:say() -- результат B: 3 -- ожидалось A: 3 b:say() -- результат B: 4 -- ожидалось B: 1
Как мне скопировать новый экземпляр таблицы, а не наследовать от старой?


Ответ

В луа таблицы при присваивании не копируются,a и b - cсылки на таблицу, одну и ту же. Чтоб создать новую таблицу вам надо реализовать функцию копирования и использовать ее:
function table.copy (originalTable) local copyTable = {} for k,v in pairs(originalTable) do copyTable[k] = v end return copyTable end
local b = table.copy(a)