Moduuli:Mallinetyokalut/hiekkalaatikko

Wikisanakirjasta

Tämän moduulin ohjeistuksen voi tehdä sivulle Moduuli:Mallinetyokalut/hiekkalaatikko/ohje

local p = {}


--- TODO: POIS ei liity mallineisiin ---->

--- Kutsuu funktiota `func` kaikille taulukon `arr` arvoille ja palauttaa tulokset taulukkona.
-- 
-- @param func: funktio muotoa f(p)
-- @param arr:  taulukko
-- @return:     taulukko
function p.map(func, arr)
    local out = {}
    for i, v in ipairs(arr) do
        out[i] = func(v)
    end
    return out
end

function p.map2(func, arr1, arr2)
    local out = {}
    local v1, v2
    
    for i = 1, math.max(#arr1, #arr2) do
        v1 = arr1[i]
        v2 = arr2[i]
        out[i] = func(v1, v2)
    end
    
    return out
end

--- Palauttaa uuden taulukon, jossa annettujen taulukoiden arvot on paritettu.
--
-- Käyttö: zip(taulukko1, taulukko2, [taulukko3 ...])
-- 
-- Esim. zip({1, 2, 3}, {"a", "b", "c"})
--  --> { {1, "a"}, {2, "b"}, {3, "c"} }
-- 
function p.zip(...)
    local out = {}
    local len = 0

    local args = {...}
    
    for i_arr, arr in ipairs(args) do
        assert(type(arr) == "table", "Argumentti " .. i_arr .. " ei ole taulukko")
        len = math.max(len, #arr)
    end

    for i_item = 1, len do
        out[i_item] = {}
        for i_arr, arr in ipairs(args) do
            out[i_item][i_arr] = arr[i_item]
        end
    end

    return out
end

function p.filter(func, arr)
    local out = {}
    for i, v in ipairs(arr) do
        if func(v) then
            out[i] = v
        end
    end
    return out
end

--- <---- POIS




--- Erottaa ryhmiä annetun taulukon avaimista etuliitteiden perusteella.
--
-- Jos etuliite sisältyy toiseen, menee arvo pisimmän etuliiteen mukaiseen ryhmään.
-- 
-- @param arr:       taulukko, esim. mallineen frame.args-parametrit
-- @param prefiksit: taulukko, jossa avaimet ovat prefiksejä ja arvot ryhmiä, joihin kyseisellä
--                   prefiksillä alkavat arvot laitetaan
-- @return:     taulukko, jossa on kullekin prefiksille oma kohta. Prefiksittömät menevät 0-alkion alle.
-- 
-- Esim. 
--   m.ryhmittele_prefikseittain({
--         ["nom.m"]  = "blörgus",
--         ["nom.f"]  = "blörga",
--         ["nom.n"]  = "blörgum",
--         ["komp.nom.m"] = "blörgämpus",
--         ["komp.nom.f"] = "blörgämpa",
--         ["komp.nom.n"] = "blörgämpum",
--         ["sup.nom.m"]  = "blöginus",
--         ["sup.nom.f"]  = "blörgina",
--         ["sup.nom.n"]  = "blörginum",
--     }, {
--         ["komp."] = "komp",
--         ["sup."] = "sup",
--  })
--  --> {
--             [0] = {
--                 ["nom.m"] = "blörgus",
--                 ["nom.f"]  = "blörga",
--                 ["nom.n"]  = "blörgum"
--             },
--             ["komp"] = {
--                 ["nom.m"] = "blörgämpus",
--                 ["nom.f"] = "blörgämpa",
--                 ["nom.n"] = "blörgämpum"
--             },
--             ["komp.testi"] = {
--                 ["nom.m"]  = "blöginus",
--                 ["nom.f"]  = "blörgina",
--                 ["nom.n"]  = "blörginum"
--             }
--     }
-- 
function p.ryhmittele_prefikseittain(arr, prefiksit)
    local out = { [0] = {} }

    local prefiksit_list = {}
    
    for prefiksi, ryhma in pairs(prefiksit) do
        assert ( type(prefiksi) == "string", "virheellinen prefiksi" )
        assert ( type(ryhma)    == "string", "virheellinen ryhmä (prefiksi: " .. prefiksi .. ")" )
        table.insert(prefiksit_list, prefiksi)
        out[ryhma] = {}
    end

    -- Lajitellaan pisimmästä lyhimpään siltä varalta, että jokin prefiksi on
    -- toisen prefiksin alku.
    table.sort(prefiksit_list, function (a, b) return #b < #a end)

    for key, val in pairs(arr) do
        local lisatty = false
        for i, prefiksi in ipairs(prefiksit_list) do
            local ryhma = prefiksit[prefiksi]
            local alku  = mw.ustring.sub(key, 1, #prefiksi)
            local loppu = mw.ustring.sub(key, #prefiksi + 1)
            
            if alku == prefiksi then
                out[ryhma][loppu] = val
                lisatty = true
                break
            end
        end
        
        if not lisatty then
            out[0][key] = val
        end
    end

    return out
end


--- Palauttaa ensimmäisen ei-tyhjän arvon annetusta luettelosta.
-- 
-- Tyhjäksi arvoksi lasketaan nil ja tyhjä merkkijono.
-- Esim. 
--   p.ensimmainen_ei_tyhja{"", nil, "arvo1", "arvo2"}
-- palauttaa "arvo1".
-- 
function p.ensimmainen_ei_tyhja(params)
    for i = 1, #params do
        local val = params[i]
        if val ~= nil and val ~= "" then
            return val
        end
    end
    
    return nil
end


--- Palauttaa uuden taulukon, jossa on annetusta taulukosta kaikki muut arvot paitsi pelkkiä tyhjiä merkkejä sisältävät.
-- Funktiolla voi poistaa malllineen parametreista anatamattomat arvot silloin kun tyhjä kenttä vastaa antamatonta arvoa.
--
-- @param params: (yksiulotteinen) taulukko
-- @return:       sama taulukko ilman tyhjiä kenttiä
--
-- Esim. poista_tyhjat({
--                        ["y.nom"] = "uugi",
--                        ["y.gen"] = "",
--                        ["y.part"] = "buugi"
-- })
-- palauttaa:
-- {
--                        ["y.nom"] = "uugi",
--                        ["y.part"] = "buugi"
-- }
function p.poista_tyhjat(params)
    local out = {}

    for k,v in pairs(params) do
		if not mw.ustring.match(v, "^%s*$") then
            if tonumber(k) == k then
                table.insert(out, v)
            else
                out[k] = v
            end
		end
    end

    return out
end






--- Erottaa pisteillä erotetut numerot nimen lopusta.
-- 
-- Apufunktio numeroidut_parametrit -funktiolle.
-- 
-- Esim. "mon1" -> "mon", 1;
--       "param2.3" -> "param", 2, 3;
--       "y3muoto1" -> "y3muoto", 1.
local function erota_osat(sana)
    -- Etsitään lopusta alkaen, jotta nimi voi sisältää numeroita.
    if type(sana) ~= "string" then
        return  { sana }
    end
    local rev  = sana:reverse()
    local nums = string.match(rev, "^([%d.]*)"):reverse()
    if nums == "" then
        return { sana }
    end
    local name = string.sub(sana, 1, sana:len() - nums:len())
    local ns   = {}
    
    for num in string.gmatch(nums, "([%d]+)") do
        table.insert(ns, tonumber(num))
    end

    return { name, unpack(ns) }
end

local function key_compare(a, b)
    local a = string.match(a or "", "([0-9]*)$")
    local b = string.match(b or "", "([0-9]*)$")

    return (tonumber(a) or 0) < (tonumber(b) or 0)
end

--- Palauttaa taulukon avaimet numerojärjestyksessä.
--
-- Huom. varmistaa vain, että numeroidut nimet ovat järjestyksessä
-- (esim. "xx1" tulee ennen "xx2"). Ei muuten nimet ovat
-- edelleen satunnaisessa järjestyksessä.
local function ordered_keys(tbl)
    local keyset = {}
    local n = 0

    for k,v in pairs(tbl) do
        n = n + 1
        keyset[n] = k
    end

    table.sort(keyset, key_compare)

    return keyset
end

--- Muuttaa assosiatiivisen taulukon, jossa avaimet on numeroitu, sisäkkäisiksi taulukoiksi.
--
-- Syötetaulukon numerot erotetaan pisteillä (paitsi ensimmäinen). Numeroimattomat ("a") ja
-- ylemmän tason ylimääräiset arvot ("c1") jätetään pois ilman virheilmoitusta.
-- Esim. { ["a"] = "X",
--         ["a2"] = "A",
--         ["b1"] = "B",
--         ["b2"] = "C",
--         ["c1"] = "X",
--         ["c1.1"] = "D" }
-- 
--    -> { ["a"] = { [2] = "A" },
--         ["b"] = { "B",
--                   "C" },
--         ["c"] = { { "D" } } }
-- @param args:  taulukko, jossa on numeroon päättyviä avaimia
-- @param nimet: taulukkoon ainakin tulevat avaimet. Numeroton arvo tulkitaan 1:seksi.
--               Esim. "mon" = "mon1" -> { "mon" = { [1] = ... } }
function p.numeroidut_parametrit(args, nimet)
    local out = {}
    local cur_node
    local path_
    local cur_p

    if nimet then
        for i, v in ipairs(nimet) do
            if args[v] then
                out[v] = {}
                out[v][1] = args[v]
            end
        end
    end

    local keys = ordered_keys(args)
    for _, key in ipairs(keys) do
        local val = args[key]
        path_ = erota_osat(key)
        cur_node = out

        if #path_ > 1 then
            -- Etsitään/luodaan polun päätepiste.
            for i = 1, (#path_ - 1) do
                cur_p = path_[i]
                
                -- Jos type on "string", on annettu kaksi eritasoista samannimistä arvoa. Ohitetaan
                -- äänettömästi, koska monissa mallineissa on esim. "mon" ja "mon2".
                if type(cur_node[cur_p]) ~= "table" then
                    cur_node[cur_p] = {}
                end
                cur_node = cur_node[cur_p]
            end
            
            table.insert(cur_node, val)
        end
    end

    return out
end

--- Yhdistää kaksi taulukkoa uudeksi taulukoksi.
-- 
-- Jos molemmissa on sama avain, tulee uuteen taulukkoon jälkimmäinen.
function p.yhdista(eka, toka)
    local out = {}
    
    for k,v in pairs(eka) do
        out[k] = v
    end
    for k,v in pairs(toka) do
        out[k] = v
    end

    return out
end


--- Luo uuden taulukon taulukon `arr` pohjalta siten, että kukin arvo esiintyy vain kerran.
--
-- Elementtien järjestys noudattaa elementtien ensimmäisiä esiintymiä.
--
-- @param arr:    taulukko
-- @param hash_f: jos taulukon `arr` elementit ovat taulukoita, annetaan tällä parametrilla
--                hajautusarvon laskeva funktio. Funktion tulee olla sellainen, että se ottaa
--                alkion parametriksi ja paluttaa joko merkkijonon tai numeron siten, että
--                kaksi samanlaista alkiota palauttavat saman arvon eikä kaksi erilaista
--                alkiota palauta samaa arvoa.
-- @return:     taulukon arr elementit tuplat poistettuna
-- 
-- Esim. 1. Alkeistyypeillä
--   poista_tuplat({ "a", "b", "c", "a", "a", "d", "b" })
--   --> { "a", "b", "c", "d" }
--
-- Esim. 2. Taulukkotyypeillä
--   m.poista_tuplat({
--             { "burger", "m" }, 
--             { "berger", "f" },
--             { "burger", "m" },
--             { "berger", "m" },
--             { "berger", "f" },
--     }, function (item) return item[1] .. " " .. item[2] end)
--  )
--  --> {
--             { "burger", "m" }, 
--             { "berger", "f" },
--             { "berger", "m" }
--     }
-- 
function p.poista_tuplat(arr, hash_f)
    local out = {}
    local seen = {}
    local key
    
    for i, val in ipairs(arr) do
        if hash_f then
            key = hash_f(val)
        else
            assert ( type(val) ~= "table", "Tarvitaan hajautusfunktio" )
            key = val
        end

        if not seen[key] then
            out[#out + 1] = val
        end
        
        seen[key] = true
    end

    return out
end

function p.sisaltyy(taulukko, arvo)
    for i, val in ipairs(taulukko) do
        if val == arvo then
            return true
        end
    end

    return false
end

return p