Created
June 21, 2013 08:32
-
-
Save moteus/5829768 to your computer and use it in GitHub Desktop.
Finds longest prefix from prefix list for given string
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
------------------------------------------------ | |
-- Общие функции для работы с деревом -- | |
------------------------------------------------ | |
local default_compare = function(lhs, rhs) return lhs == rhs end; | |
local default_char_set = {'0','1','2','3','4','5','6','7','8','9'}; | |
local INVALID_VALUE_ALWAYS_NIL = {} | |
--Удаляет все пустые ветви в дереве | |
local pack_empty | |
pack_empty = function(t) | |
for k,v in pairs(t) do | |
if type(k) ~= 'boolean' then | |
if not pack_empty(v) then | |
t[k] = nil | |
end | |
end | |
end | |
if next(t) ~= nil then | |
return true | |
end | |
end | |
-- Поглащение длинных префиксов более короткими | |
local pack_dup = function(t, compare_value) | |
compare_value = compare_value or default_compare | |
local do_work | |
do_work = function(t, v, val_prefix, prefix) | |
prefix = prefix or '' | |
val_prefix = val_prefix or '' | |
if t[true] ~= nil then | |
if compare_value(v, t[true], val_prefix, prefix) then | |
t[true] = nil | |
else | |
v = t[true] | |
val_prefix = prefix | |
end | |
end | |
for k,r in pairs(t) do | |
if type(k) ~= 'boolean' then | |
do_work(r, v, val_prefix, prefix .. k) | |
end | |
end | |
end | |
do_work(t) | |
end | |
-- "Сворачивает" ветки поддерева | |
--[[ | |
@param pack_percent | |
Если есть следующие префиксы : 10,11,12,13,14,15,16,17,18 с одинаковыми значениями то их | |
возможно заменить на один префикс: 1, но при этом необходимо создать префикс 19. | |
Значение этого префикса должно быть либо значением префикса 1(если он существовал до сворачивания) | |
либо принемать невалидное значение. При этом если невалидное значение отсутствует, то свертка | |
оказывается невозможной. | |
Такая замена имеет смысл если результирующее число префиксов будет меньше и если | |
существует возможность создания невалидных значений. | |
Этот параметр указывает какой процент префиксов сворачивается (в примере сворачивается 90 процентов) | |
если установить 100, то будут сворачиватся только все префиксы и невалидные значения вставлятся не будут | |
@param invalid_value | |
невалидное значение | |
если оно равно INVALID_VALUE_ALWAYS_NIL, то это приведет к запрету переноса значения с короткого префикса | |
на длинный. Например, если рассмотреть предыдущий пример, и предположить что префикс 1 имеет | |
некоторое значение, то в случае установки этого значения сворачивание будет невозможно. Это предотвращает | |
создание ошибочных ситуаций когда номер равный короткому префиксу(1) до сворачивания имел одно значение, | |
а после сворачивания другой | |
@param compare_value | |
функция сравнения двух значенй. По умолчанию применяется operator== | |
@param char_set | |
полное множество значений узлов если у узла есть подчиненные узлы которые покрывают это множество | |
и эти узлы имеют одинаковое значение то это значение можно перенести на этот узел. | |
По умолчанию цыфры от 0 до 9 | |
@note | |
Следует учитывать следующие эффект | |
Если есть префиксы : 1[0..9] с одинаковыми значениями, то номеру '1' не соответствует ни одно значение. | |
После сворачивания в префикс 1 этот номер принимает действительное значение. | |
Тот же вариант если есть префикс 1 с одним значениеим и префиксы 11[0..9] с другим, то до свертки номеру 11 соответствует | |
значение префикса 1, а после свертки значение префикса 11. | |
В таком случае создается еще одно значение [false] которое соответствует старому значению для этого номера. | |
Это относится к номеру равному префиксу к которому производится свертка. Свертка может быть произведена если | |
этот номер не является верным. | |
--]] | |
local pack_roll = function(t, pack_percent, invalid_value, compare_value, char_set) | |
pack_percent = tonumber(pack_percent) | |
if pack_percent == nil then | |
return | |
end | |
compare_value = compare_value or default_compare | |
char_set = char_set or default_char_set | |
local do_work | |
do_work = function(t, prfx, val) | |
for k,v in pairs(t) do | |
if type(k) ~= 'boolean' then | |
do_work(v, (prfx or '') .. k, v[true] or val) | |
end | |
end | |
if prfx == nil then | |
return | |
end | |
-- таблица для подсчета количества узлов для разных значений | |
local node = {} | |
-- в node заносим количество префиксов для каждого значения | |
for _, key in ipairs(char_set) do repeat | |
if (t[key] == nil) or (t[key][true] == nil) then | |
break --continue | |
end | |
local value = t[key][true] | |
local flag = false | |
for i,j in pairs(node)do | |
if compare_value(value, i) then | |
node[i] = node[i] + 1 | |
flag = true | |
end | |
end | |
-- Нет такого значения в node | |
if not flag then | |
node[value] = 1; | |
end | |
until true end | |
-- Находим значение с максимальным числом повторений | |
local max_val | |
for i,j in pairs(node)do | |
if max_val == nil then | |
max_val = i | |
elseif node[max_val] < j then | |
max_val = i | |
end; | |
end; | |
--Если свертка не целесообразна | |
if | |
(max_val == nil) or | |
(node[max_val] < (#char_set * (pack_percent / 100 ))) | |
then | |
return nil | |
end | |
-- Предотвращает создание длинных префиксов | |
if invalid_value == INVALID_VALUE_ALWAYS_NIL then | |
val = nil | |
end | |
-- если нет 'верхнего' значения то можно сворачивать только целиком. | |
-- например есть префиксы 1[0..9]->aaa то их можно свернуть в 1->aaa | |
-- но если есть только 1[0..8]->aaa и для 1 нет значения и не определено invalid_value | |
-- то свертка 1->aaa невозможна | |
-- но если определен еще префикс 19->bbb то возможно 1->aaa 19->bbb | |
local total_ = 0 | |
table.foreach(node,function(_,v) total_ = total_ + v end) | |
if val == nil and total_ < #char_set then | |
return | |
end | |
t[true] = max_val | |
-- Это значение для полного соответствия префиксу | |
t[false] = val | |
--свертка | |
for _, key in ipairs(char_set) do repeat | |
-- Нет поддерева - создаем | |
if t[key] == nil then | |
t[key] = {} | |
end | |
-- Нет значения - присваиваем значение более короткого префикса | |
if t[key][true] == nil then | |
assert(pack_percent < 100) | |
assert(val) | |
t[key][true] = val | |
break --continue | |
end | |
--Удаляем префиксы с максимальным количеством повторений | |
if compare_value(t[key][true], max_val) then | |
t[key][true] = nil; | |
end | |
until true end | |
return true | |
end | |
do_work (t, nil, invalid_value) | |
pack_empty(t) | |
end | |
-- Возвращает поддерево | |
local function sub_tree(t, key) | |
for i = 1, #key do | |
local b = string.sub(key, i, i) | |
t = t[b] | |
if not t then | |
return nil | |
end | |
end | |
return t | |
end | |
-- Итератор | |
local function for_each(t, func) | |
if type(func) ~= 'function' then | |
return | |
end | |
local do_work | |
do_work = function (t, name) | |
if t[true] ~= nil then | |
func(name, t[true], true) | |
end | |
if t[false] ~= nil then | |
func(name, t[false], false) | |
end | |
for k, r in pairs(t) do | |
if type(k) ~= 'boolean' then | |
do_work(r, name .. k) | |
end | |
end | |
end | |
do_work(t, '') | |
end | |
-- Итератор | |
local function for_each_sort(t, char_set, func) | |
if (type(char_set) == 'function')and(func == nil) then | |
func = char_set | |
char_set = default_char_set | |
end | |
if type(func) ~= 'function' then | |
return | |
end | |
local do_work | |
do_work = function (t, name) | |
if t[true] ~= nil then | |
func(name, t[true], true) | |
end | |
if t[false] ~= nil then | |
func(name, t[false], false) | |
end | |
for _, k in ipairs(char_set) do | |
assert(type(k) ~= 'boolean') | |
local r = t[k] | |
if r then | |
assert(type(r) == 'table') | |
do_work(r, name .. k) | |
end | |
end | |
end | |
do_work(t, '') | |
end | |
-- Итератор | |
local function transform(t, func) | |
if type(func) ~= 'function' then | |
return | |
end | |
local do_work | |
do_work = function (t, name) | |
if t[true] ~= nil then | |
t[true] = func(name, t[true], true) | |
end | |
if t[false] ~= nil then | |
t[false] = func(name, t[false], false) | |
end | |
for k, r in pairs(t) do | |
if type(k) ~= 'boolean' then | |
do_work(r, name .. k) | |
end | |
end | |
end | |
do_work(t, '') | |
end | |
--- | |
-- Индексированный поиск по префиксам | |
-- при вводе полного слова осуществляется поиск значения соответствующий | |
-- сомому длинному префиксу | |
-- | |
local tree_index = {} | |
--- | |
-- Добавление префикса и значения | |
-- существующее значение перезаписывается | |
-- flag - true - значение для префикса | |
-- - false - префикс это не префикс а полная строка | |
function tree_index:add( key, val, flag ) | |
if flag == nil then flag = true end | |
assert(type(flag) == 'boolean') | |
local t = self.data | |
for i = 1, #key do | |
local b = key:sub(i,i) | |
if not t[b] then | |
t[b] = {} | |
end | |
t = t[b] | |
end | |
t[flag] = val | |
end | |
--- | |
-- Поиск значения на совпадение самого длинного ключа | |
-- use_false - по умолчанию включен | |
-- если у префикса есть 2 значения. | |
-- брать второе если ключ полностью соответствует префиксу | |
-- и первое если ключ длиннее префикса. | |
-- при use_false = false всегда берется только первое значение | |
-- return value, prefix, flag | |
function tree_index:find( key , use_false) | |
if use_false == nil then | |
use_false = true | |
end | |
local real_key = '' | |
local flag = true | |
local t = self.data | |
local value = t[true] | |
for i = 1, #key do | |
local b = string.sub(key, i, i) | |
t = t[b] | |
if not t then | |
break | |
end | |
if use_false and i == #key and t[false] ~= nil then | |
value = t[false] | |
flag = false | |
real_key = string.sub(key, 1, i) | |
elseif t[true] ~= nil then | |
value = t[true] | |
flag = true | |
real_key = string.sub(key, 1, i) | |
end | |
end | |
if (value == nil) or (value == self.invalid_value) then | |
return nil | |
end | |
return value, real_key, flag | |
end | |
--- | |
-- Удаление значения | |
function tree_index:del( key ) | |
local t = self.data | |
for i = 1, #key do | |
local b = string.sub(key, i, i) | |
t = t[b] | |
if not t then | |
return false | |
end | |
end | |
t[true] = nil | |
return true | |
end | |
--- | |
-- Итератор | |
function tree_index:for_each( func ) | |
for_each(self.data, func) | |
return self | |
end | |
function tree_index:for_each_sort( char_set_or_func, func_or_nil ) | |
for_each_sort(self.data, char_set_or_func, func_or_nil) | |
return self | |
end | |
function tree_index:pack_dup(compare_value) | |
return pack_dup(self.data, compare_value) | |
end | |
function tree_index:pack_empty() | |
return pack_empty(self.data) | |
end | |
function tree_index:pack_roll(pack_percent, compare_value, char_set) | |
return pack_roll( | |
self.data, | |
pack_percent, | |
self.invalid_value, | |
compare_value, | |
char_set | |
) | |
end | |
--- | |
-- Минимизирует префиксное поле | |
function tree_index:pack( pack_percent, compare_value, char_set ) | |
self:pack_dup(compare_value) | |
self:pack_empty() | |
self:pack_roll(pack_percent, compare_value, char_set) | |
end | |
--- | |
-- возаращает часть дерева | |
-- копирования не происходит | |
function tree_index:sub_tree( key ) | |
local data = sub_tree(self.data, key) | |
if not data then | |
return data | |
end | |
local t = {data = data} | |
table.foreach(self, function(k,v) | |
if t[k] == nil then | |
t[k] = v | |
end | |
end) | |
return t | |
end | |
function tree_index:transform(func) | |
transform(self.data, func) | |
return self | |
end | |
function tree_index:clear() | |
self.data = {} | |
end | |
--- | |
-- | |
function tree_index:keys() | |
local t = {} | |
self:for_each(function(prefix) | |
table.insert(t,prefix) | |
end) | |
return t | |
end | |
--- | |
-- возвращает множество ключей | |
function tree_index:key_set(val) | |
val = (val ~= nil) and val or true | |
local t = {} | |
self:for_each(function(prefix) | |
t[prefix] = val | |
end) | |
return t | |
end | |
--- | |
-- | |
function tree_index:values() | |
local t = {} | |
self:for_each(function(prefix,val) | |
t[prefix] = val | |
end) | |
return t | |
end | |
--- | |
-- | |
function tree_index:set_values(t) | |
self:clear() | |
for prefix,value in pairs(t)do | |
self:add(prefix,value) | |
end | |
return t | |
end | |
--- | |
-- | |
function tree_index:exists( key ) | |
local _, pfx = self:find(key) | |
return pfx == key | |
end | |
--- | |
-- расширяет по возможности набор ключей до | |
-- указоного списка | |
-- @param clone = функция для клонирования значения | |
-- | |
function tree_index:expand( keys, clone ) | |
if clone then | |
for _,newkey in ipairs(keys) do | |
local val, key = self:find(newkey) | |
if val and (newkey ~= key) then | |
self:add(newkey, clone(val)) | |
end | |
end | |
else | |
for _,newkey in ipairs(keys) do | |
local val, key = self:find(newkey) | |
if val and (newkey ~= key) then | |
self:add(newkey, val) | |
end | |
end | |
end | |
end | |
------------------------------------------------------------------- | |
-- Реализация загрузки префиксов из файла | |
------------------------------------------------------------------- | |
require 'lpeg' | |
require 're' | |
local function try(f, catch_f) | |
local ok, exception = pcall(f) | |
if not ok then | |
if catch_f then catch_f(exception) else error(exception) end | |
end | |
end | |
local function fitstr(str, ch, width) | |
if #str >= width then | |
return str | |
end | |
return string.rep(ch,width-#str) .. str | |
end | |
--- | |
-- Уменьшает диапазон удаляя последние символы из префиксов | |
-- если они покрывают весь диапазон | |
-- | |
-- 74957252300-74957252399 -> 749572523 | |
-- | |
local function pack_range(be, en, first_cahr, last_cahr) | |
if #be ~= #en then | |
return be, en | |
end | |
first_cahr = first_cahr or '0' | |
last_cahr = last_cahr or '9' | |
local last_be, last_en = string.sub(be, -1, -1), string.sub(en, -1, -1) | |
while(last_be == first_cahr) do | |
if last_en == last_cahr then | |
be, en = string.sub(be, 1, -2), string.sub(en, 1, -2) | |
last_be, last_en = string.sub(be, -1, -1), string.sub(en, -1, -1) | |
else | |
break | |
end | |
end | |
return be, en | |
end | |
local DecodePrefixList | |
local DecodeAppend | |
do | |
local function list(t) | |
t.subprefix=nil | |
return { | |
name = "list"; | |
value = t; | |
} | |
end | |
local function append(t) | |
t.subprefix=nil | |
return { | |
name = "append"; | |
value = t; | |
} | |
end | |
local function append_tst(t) | |
t.subprefix=nil | |
return { | |
name = "append"; | |
value = t; | |
} | |
end | |
function serialize(...) | |
require "sys" | |
sys.serialize({...},"SERIALIZE") | |
end | |
local PrefixList_pat = re.compile( | |
[=============================================================================[ | |
mian <- <sp>* <str> <sp>* <eos> | |
str <- (<list> (<lstdelim> <list>)*) ->{} | |
list <- <list1> ->{} | |
list1 <- ( | |
<applist> ->{} ->append | |
<listelem> ->{} ->list | |
) / -- 7 4 9 5,6 -> append{7,4,9} list{5,6} | |
(<applist> <elem>) ->{} ->append / -- 7 495 -> append{7,495} | |
<listelem> ->{} ->list / -- 7,495 -> list{7,495} | |
<elem> ->{} ->list -- 7495 -> list{7495} | |
applist <- ( <elem> <appdelim>)+ -- список главных префиксов. может существовать | |
-- только при наличии подчененных элементов | |
listelem <- ('('<sp>* <listelem1> <sp>*')') / <listelem1> -- список элементов | |
listelem1 <- ((<elem> <elmdelim>)+ <elem>) | |
elem <- ('(' <sp>* <elem1> <sp>* ')') / <elem1> -- элемент списка диапазон или одиночный элемент | |
elem1 <- <range> / <single> | |
range <- {:subprefix: %a+(%d+%a+)+ / (%a*) :} | |
( | |
{:beg:%d+:} | |
<sp>* [-] <sp>* | |
{:sub:=subprefix:} {:en:%d+:} | |
) -> {} | |
single <- {%w*} | |
appdelim <- <sp> !(<elmdelim> / <lstdelim> / <eos>) -- Отделяет главные префиксы | |
lstdelim <- (<sp>* [;] <sp>*) -- Отделяет независимые списки | |
elmdelim <- (<sp>* [,] <sp>*) -- Отделяет элементы списка | |
sp <- ' '+ | |
eos <- !. | |
]=============================================================================], | |
{list=list,append=append,appendtst=append_tst,print=serialize}) | |
function DecodePrefixList(str) | |
local t = lpeg.match(PrefixList_pat, str) | |
if t then | |
for i = 1,#t do | |
for j = 1, #t[i] do | |
local name = t[i][j].name | |
local value = t[i][j].value | |
t[i][name] = value; | |
t[i][j] = nil | |
end | |
end | |
end | |
return t | |
end | |
function DecodeAppend(append,pack_range) | |
local new_append = {''} | |
if append == nil then | |
return new_append | |
end | |
for i,prefix in ipairs(append) do | |
if type(prefix) == 'string' then | |
-- добавляем новый префикс ко всем предыдущим | |
for k,v in ipairs(new_append) do | |
new_append[k] = v .. prefix | |
end | |
else | |
local sub_prefix, beg, en = assert(prefix.sub), assert(prefix.beg), assert(prefix.en) | |
if pack_range and (append[i+1] == nil) then | |
beg, en = pack_range(beg, en) | |
end | |
local len = math.max(#beg, #en) | |
if len > 0 then | |
local t = {} | |
for i = beg, en do | |
local s = sub_prefix .. fitstr(tostring(i), '0', len) | |
for k,v in ipairs(new_append) do | |
table.insert(t,v..s) | |
end | |
end | |
new_append = t | |
end | |
end | |
end | |
return new_append | |
end | |
do -- test -- | |
local cmp_t | |
local function cmp_v(v1,v2) | |
local flag = true | |
if type(v1) == 'table' then | |
if type(v2) == 'table' then | |
flag = cmp_t(v1, v2) | |
else | |
flag = false | |
end | |
else | |
flag = (v1 == v2) | |
end | |
return flag | |
end | |
function cmp_t(t1,t2) | |
for k in pairs(t2)do | |
if t1[k] == nil then | |
return false | |
end | |
end | |
for k,v in pairs(t1)do | |
if not cmp_v(t2[k],v) then | |
return false | |
end | |
end | |
return true | |
end | |
local function dump (pat) | |
require "sys" | |
local t = DecodePrefixList(pat) | |
sys.serialize(t,'"' .. pat .. '"') | |
io.write"\n" | |
end | |
local function dump2 (pat) | |
require "sys" | |
local t = DecodePrefixList(pat) | |
t = DecodeAppend(assert(t[1].append),pack_range) | |
sys.serialize(t,'"' .. pat .. '"') | |
io.write"\n" | |
end | |
-- dump"7 4 9 (5,6) 7" | |
-- dump2"999 1-5 7-8" | |
local tests = {} | |
local tests_index={} | |
local function tclone(t) | |
local res = {} | |
for k,v in pairs(t)do res[k]=v end | |
return res | |
end | |
local test = function(str, result) | |
local t | |
if type(result) == 'string' then | |
local res = assert(tests_index[str]) | |
t = {result, result = res.result} | |
assert(result ~= str) | |
tests_index[result] = t; | |
else | |
t = {str,result=result} | |
tests_index[str] = t; | |
end | |
return table.insert(tests,t) | |
end | |
--test = function()end | |
test("7 4 9 5,6", | |
{{ | |
append = {"7","4","9"}; | |
list = {"5","6"}; | |
}} | |
) | |
test("7 4 9 5,6", "7 4 9 (5,6)" ) | |
test("7 4 9 5,6", "7 4 9 (5 ,6)" ) | |
test("7 495", | |
{{ | |
append = {"7","495"}; | |
}} | |
) | |
test("7495", | |
{{ | |
list = {"7495"}; | |
}} | |
) | |
test("7,495", | |
{{ | |
list = {"7","495"}; | |
}} | |
) | |
test("749 5 - 6", | |
{{ | |
append={ | |
[1]="749"; | |
[2]={ | |
beg="5"; | |
sub=""; | |
en="6"; | |
}; | |
}; | |
}} | |
) | |
test("749 5 - 6", "749 (5 - 6)") | |
test("749 5 - 6", "749 (5-6)" ) | |
test("749 5 - 6", "749 ( 5-6 )") | |
test("7 5 1-5 7-9", | |
{{ | |
append={ | |
[1]="7"; | |
[2]="5"; | |
[3]={ | |
sub=""; | |
en="5"; | |
beg="1"; | |
}; | |
[4]={ | |
sub=""; | |
en="9"; | |
beg="7"; | |
}; | |
}; | |
}} | |
); | |
test("7 5 1-5 7-9", "7 5 1- 5 7-9") | |
test("7 5 1-5 7-9", "7 5 1 -5 7-9") | |
test("7 5 1-5 7-9", "7 5 1 - 5 7-9") | |
test("7 5 1-5 7-9", "7 5 1 - 5 7 - 9") | |
test("7 5 1-5 7-9", "7 5 1-5 7 - 9") | |
test("7 5 1-5 7-9", "7 5 (1-5) 7-9") | |
test("7 5 1-5 7-9", "7 5 ( 1-5) 7-9") | |
test("7 5 1-5 7-9", "7 5 ( 1-5 ) 7-9") | |
test("7 5 1-5 7-9", "7 5 ( 1 - 5 ) 7-9") | |
test("7 5 1-5 7-9", "7 5 ( 1 - 5 ) (7-9)") | |
---------------------------------- | |
test"7 4 9 (5,6) 7" | |
test"7 4 9 (5,6),7" | |
for _,test_case in ipairs(tests)do | |
local t = DecodePrefixList(test_case[1]) | |
assert(cmp_v(t, test_case.result ), test_case[1]) | |
-- проверяем разделение на списки | |
local str = test_case[1] .. ';' .. test_case[1] | |
t = DecodePrefixList(str) | |
local res | |
if test_case.result ~= nil then | |
local n = #test_case.result | |
res = {} | |
for i = 1,n do | |
res[i] = test_case.result[i] | |
res[2*i] = test_case.result[i] | |
end | |
assert(#res == 2*n) | |
end | |
assert(cmp_v(t, res ), str) | |
end | |
end -- test | |
end | |
local function ProcessPrefixes(functor, t, value, prefix_str, pack_range) | |
for _, prefix in ipairs(t.list) do | |
if type(prefix) == 'string' then | |
for _,main_prefix in ipairs(t.append) do | |
functor(main_prefix .. prefix, value, prefix_str) | |
end | |
else | |
local sub_prefix, beg, en = assert(prefix.sub), assert(prefix.beg), assert(prefix.en) | |
if pack_range then | |
beg, en = pack_range(beg, en) | |
end | |
local len = math.max(#beg, #en) | |
for i = beg, en do | |
for _,main_prefix in ipairs(t.append) do | |
local s = main_prefix .. sub_prefix .. fitstr(tostring(i), '0', len) | |
functor(s, value, prefix_str) | |
end | |
end | |
end | |
end | |
end | |
local function LoadPrefixFromFile_impl(FileName_or_file, functor, pack_range) | |
local data_pat = re.compile"[ ]*(({[^\t]+}[\t]?)/({[^\t]*}[\t])){.*}" | |
local file, do_close | |
if type(FileName_or_file) == 'string' then | |
file = assert(io.open(FileName_or_file,'r'),'Can not open file "' .. FileName_or_file .. '"!') | |
do_close = true | |
else | |
file = assert(FileName_or_file) | |
end | |
try(function() | |
for str in file:lines() do | |
local prefixes, value = lpeg.match(data_pat, str) | |
if prefixes then -- строка не пустая | |
local t = assert(DecodePrefixList(prefixes), "Error prefix: " .. (prefixes or '<EMPTY>')) | |
for _,p in ipairs(t) do | |
if p.list == nil then | |
p.list = {''} | |
p.append = DecodeAppend(p.append, pack_range) | |
else | |
p.append = DecodeAppend(p.append) | |
end | |
ProcessPrefixes(functor, p, value, prefixes, pack_range) | |
end | |
end | |
end | |
if do_close then file:close() end | |
end, | |
function(e) --catch | |
if do_close then file:close() end | |
error(e) | |
end) | |
end | |
--- | |
-- fn pack_range - функция для сжатия диапазонов | |
-- bool ret_list - возвращать список префикс => строка из которой он сформирован | |
-- | |
function tree_index:load(FileName, pack_range, ret_list) | |
local prefix_list_t | |
local add_prefix | |
if ret_list then | |
prefix_list_t = {} | |
add_prefix = function(prefix, value, list) | |
self:add(prefix,value) | |
prefix_list_t[prefix] = list | |
end | |
else | |
add_prefix = function(prefix, value)self:add(prefix,value)end | |
end | |
LoadPrefixFromFile_impl(FileName, add_prefix, pack_range) | |
return prefix_list_t | |
end | |
local function LoadPrefixFromFile(...) | |
local tree = tree_index:new() | |
local prefix_list_t = tree:load(...) | |
return tree, prefix_list_t | |
end | |
--- | |
-- cnt | |
function tree_index:new() | |
local t = setmetatable({ | |
data = {}; | |
invalid_value = {}; | |
char_set = default_char_set; | |
}, {__index = self}) | |
return t; | |
end | |
--- | |
-- serialize | |
function tree_index:serialize(packer) | |
return packer{ | |
data = self.data; | |
invalid_value = self.invalid_value; | |
char_set = self.char_set; | |
} | |
end | |
--- | |
-- deserialize | |
function tree_index:deserialize(unpacker, str) | |
local t = unpacker(str) | |
if not(t.data and t.char_set) then | |
return nil, "invalid format" | |
end | |
return setmetatable({ | |
data = t.data; | |
invalid_value = t.invalid_value; | |
char_set = t.char_set; | |
}, {__index = self}) | |
end | |
local type, assert = | |
type, assert | |
local _M = {} | |
function _M.new() | |
return tree_index:new() | |
end | |
function _M.deserialize(...) | |
return tree_index:deserialize(...) | |
end | |
_M.for_each, _M.sub_tree, _M.pack_dup, _M.pack_empty, _M.pack_roll, _M.transform = | |
for_each, sub_tree, pack_dup, pack_empty, pack_roll, transform | |
_M.pack_range = | |
pack_range | |
_M.INVALID_VALUE_ALWAYS_NIL = INVALID_VALUE_ALWAYS_NIL | |
_M.LoadPrefixFromFile = LoadPrefixFromFile | |
return _M |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment