Модуль:Traversal
Материал из Указатель частей и соединений РККА 1941-1945
Для документации этого модуля может быть создана страница Модуль:Traversal/doc
--[==[ Нормализованный обход дерева детей. Отдает отдельно «начало даты» «начало уровня» «начало таба», «конец таба»... Пока не делаем итератором. Но вложенного использования не бывает. Все использование за один фрейм, глобальные не делаем. Функции init, get --]==] -- tree traversal local tt = { } local RowN -- номер обрабатываемой строка local State -- надо что-то хранить о статусе, потому что из одной строки вылезает куча ответов local Query -- обрабаываемый запрос --local Father --local Gene local Prev -- содержимое предыдущего ряда local LPrev -- глубина стека предыдущего ряда local wasValue -- надо ли давать цезуру из-за смены отца function tt.init( query, father ) RowN = 0 State = {} Query = query -- Father = father -- Gene = string.match( father, '^[^(]+ %(([^()]+)%)' ) -- if type( report.knownBrackets[Gene] ) ~= 'string' then -- Gene = nil -- end Prev = { ['tab-1'] = Query[1].tab0 } LPrev = -1 end function tt.get( para ) --[=[ между двумя элементами нам надо: -6. завершить корпусные уровни -5. завершить таб -4. завершить ряд табов -3. завершить фронтовые уровни -2. завершить родителя -1. завершить дату 1. открыть дату 2. открыть родителя 3. открыть фронтовые уровни 4. открыть ряд табов 5. открыть таб 6. открыть корпусные уровни Отдает: { ']' } — закрытие таба { ']]' } — закрытие ряда { '}' } — закрытие даты { ')', false } — закрытие корпуса { ')', true } — закрытие армии { '|' } — смена папы { '{', дата } — смена даты { '[[' } — открытие ряда { '[',таб } — открытие таба { '(',n } — открытие армии уровня n { '(', nil } — открытие корпуса { i, row, (есть дети?) } --]=] if next( State ) then local x = State[1] table.remove( State, 1 ) return x else RowN = RowN + 1 local row = para or Query[RowN] if not row then if RowN > #Query+1 then return nil else row = { tab0 = Query[1].tab0 } end end local diff if row.date ~= Prev.date then diff = -1 else for i = 0, LPrev do if Prev['porno'..i] ~= row['porno'..i] then diff = i break end end -- поскольку sql отдает только оконечные узлы, один результат не может полностью включать в себя другой assert( diff, 'Неконсистентное состояние БД, два эквивалентных элемента ' .. RowN ) end -- diff <= LPrev всегда. Это номер позиции в стеке, с которой начинается расхождение. -- Он равен -1, если расхождение в дате. Он равен нулю, если у дерева неоднозначный отец (если юнит в таблицах встречается дважды). -- diff == 0 и diff == -1 нужно обрабатывать особо; табы при этом закрываем. -- корпусные и армейские закрывающие скобки могут потребоваться, если -- LPrev > 1 и diff ~= LPrev. Больше 1, потому что дети верхнего уровня не в скобках local prevtab = Prev['tab' .. LPrev] if LPrev > 1 and diff ~= LPrev then -- LPrev == 1 & diff = 0 — у нас был подчиненный, а теперь новое значение юнита без детей. -- Скобку закрыть надо, если мы считаем, что дети исходного юнита заключаются в скобки. Но нет. -- Поэтому нас интересуют только случаи, когда дети второго уровня local h = LPrev local limit = math.max( 1, diff ) while h > limit do h = h - 1 if Prev['ass'..h] ~= '' then -- если дошли до уровня армий, то может требоваться принудительное закрытие таба и ряда табов if prevtab ~= '' then table.insert( State, { ']' } ) table.insert( State, { ']]' } ) -- ' .. table.concat( {LPrev,diff,h}, '/' ) ) prevtab = '' end h = h + 1 -- чтобы вернуться к циклу break end table.insert( State, { ')' } ) end if h ~= limit then -- если вышли по break, то закрываем таб и ряд (при условии, что они были) -- if prevtab ~= '' and Prev['ass'..h] ~= '' then -- table.insert( State, ']' ) -- table.insert( State, ']] ' .. table.concat( {LPrev,diff,h}, '/' ) ) -- prevtab = '' -- end -- h = h + 1 -- чтобы вернуться к циклу end while h > limit do h = h - 1 table.insert( State, { ')', true } ) end end if diff == -1 and RowN ~= 1 then if prevtab ~= '' and row.tab0 == '' then table.insert( State, { ']' } ) table.insert( State, { ']]' } ) prevtab = '' end table.insert( State, { '}' } ) elseif diff == 0 then if prevtab ~= '' and row.tab0 == '' then table.insert( State, { ']' } ) table.insert( State, { ']]' } ) prevtab = '' end if wasValue then table.insert( State, { '|' } ) wasValue = nil end end if RowN > #Query then return tt.get() end --TODO: выход if diff == -1 then table.insert( State, { '{', row.date } ) wasValue = nil end LPrev = 0 for i = math.max( diff, 1 ), 6 do if row['porno'..i] == '' then LPrev = i-1 break end if row['ass'..i] ~= '' then if prevtab ~= '' then table.insert( State, { ']' } ) table.insert( State, { ']]' } ) prevtab = '' end elseif prevtab ~= row['tab'..i] then if prevtab == '' then table.insert( State, { '[[' } ) else table.insert( State, { ']' } ) end table.insert( State, { '[', row['tab'..i] } ) prevtab = row['tab'..i] end table.insert( State, { i, row, ( row['porno'..(i+1)] or '' ) ~= '' } ) wasValue = true if ( row['porno'..(i+1)] or '' ) ~= '' then if row['ass'..i] ~= '' then table.insert( State, { '(', row['ass'..i] } ) else table.insert( State, { '(' } ) end end end Prev = row end return tt.get() end function tt.Test( unit ) -- local unit = mw.text.trim( --[[ frame.args[1] or frame:getParent().args[1] or --]] '20 армия' ) local query_tables = 'units=t0, units=t1, units=t2, units=t3, units=t4, units=t5, units=t6, ' .. --[['units=t0s,]] 'units=t1s, units=t2s, units=t3s, units=t4s, units=t5s, units=t6s' local query_fields = 't0.fix=date, t0.war=war, ' .. 't0.unit=id0, t0.addendum=add0, t0.ref=ref0, t0.creating=form0, t0.ass=ass0, t0.tab=tab0, t0.hq=hq0, t0.fixunit=porno0, ' -- t0s.unit=s_id0, t0s.addendum=s_add0, t0s.ref=s_ref0, ' .. 't1.unit=id1, t1.addendum=add1, t1.ref=ref1, t1.creating=form1, t1.ass=ass1, t1.tab=tab1, t1.hq=hq1, t1.fixunit=porno1, t1s.unit=s_id1, t1s.addendum=s_add1, t1s.ref=s_ref1, ' .. 't2.unit=id2, t2.addendum=add2, t2.ref=ref2, t2.creating=form2, t2.ass=ass2, t2.tab=tab2, t2.hq=hq2, t2.fixunit=porno2, t2s.unit=s_id2, t2s.addendum=s_add2, t2s.ref=s_ref2, ' .. 't3.unit=id3, t3.addendum=add3, t3.ref=ref3, t3.creating=form3, t3.ass=ass3, t3.tab=tab3, t3.hq=hq3, t3.fixunit=porno3, t3s.unit=s_id3, t3s.addendum=s_add3, t3s.ref=s_ref3, ' .. 't4.unit=id4, t4.addendum=add4, t4.ref=ref4, t4.creating=form4, t4.ass=ass4, t4.tab=tab4, t4.hq=hq4, t4.fixunit=porno4, t4s.unit=s_id4, t4s.addendum=s_add4, t4s.ref=s_ref4, ' .. 't5.unit=id5, t5.addendum=add5, t5.ref=ref5, t5.creating=form5, t5.ass=ass5, t5.tab=tab5, t5.hq=hq5, t5.fixunit=porno5, t5s.unit=s_id5, t5s.addendum=s_add5, t5s.ref=s_ref5, ' .. 't6.unit=id6, t6.addendum=add6, t6.ref=ref6, t6.creating=form6, t6.ass=ass6, t6.tab=tab6, t6.hq=hq6, t6.fixunit=porno6, t6s.unit=s_id6, t6s.addendum=s_add6, t6s.ref=s_ref6, ' .. 't0._pageName=page, t0.page=bookpage' local query_args = { join = 't0.fixunit=t1.fixparent, ' -- t0.fixstruct=t0s.fixunit, ' .. 't1.fixunit=t2.fixparent, t1.fixstruct=t1s.fixunit, ' .. 't2.fixunit=t3.fixparent, t2.fixstruct=t2s.fixunit, ' .. 't3.fixunit=t4.fixparent, t3.fixstruct=t3s.fixunit, ' .. 't4.fixunit=t5.fixparent, t4.fixstruct=t4s.fixunit, ' .. 't5.fixunit=t6.fixparent, t5.fixstruct=t5s.fixunit, ' .. 't6.fixstruct=t6s.fixunit ', where = 't0.unit="' .. unit .. '"', orderBy = 'date, porno1, porno2, porno3, porno4, porno5, porno6', limit = 5000, } local children = mw.ext.cargo.query( query_tables, query_fields, query_args ) mw.log( #children .. ' ------ ' ) tt.init( children, unit ) local result = {} repeat local u, c = tt.get() if not u then break else c = u[1] if type( c ) == 'number' then c = u[2]['id'..c] end end table.insert( result, c ) if #result == 5000 then break end until not u return '[' .. #result .. '] : ' .. table.concat( result, ' : ' ) .. ' : END ' end return tt