Lua - Использование string.find для возврата true, если между 2 специальными символами - PullRequest
0 голосов
/ 08 февраля 2019

Мне нужен шаблон, который будет работать с string.find (или string.match, если необходимо), который будет возвращать true, если строка «пути к таблице» соответствует.Это моя функция:

local function FindValueFromPattern(myTable, pattern, previousPath)
    for key, value in pairs(myTable) do
        local path;

        if (not previousPath) then
            path = key;
        else
            path = string.format("%s.%s", previousPath, key);
        end

        if (path:find(pattern)) then
            return value;

        elseif (type(value) == "table") then
            value = FindValueFromPattern(value, pattern, path);

            if (value ~= nil) then
               return value;
            end
        end
    end

    return nil;
end

local tbl = {}
tbl.settings = {};
tbl.settings.module = {};
tbl.settings.module.appearance = {};
tbl.settings.module.appearance.color = "blue";

print(FindValueFromPattern(tbl, "settings.module.appearance.color")); -- prints "blue";

Приведенный выше код работает, НО я хочу изменить шаблон на:

"module.<ANY>.color", где <ANY> - любая дочерняя таблица "модуля" итакже имеет дочернюю таблицу с именем «color», поэтому при обходе таблицы будет возвращено значение независимо от того, какая таблица используется (не обязательно должна быть таблицей внешнего вида):

-- should also print "blue" ("setting." should not be required);
print(FindValueFromPattern(tbl, "module.<ANY>.color"));

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

Так что вопроскак будет выглядеть этот шаблон?Спасибо.

Ответы [ 2 ]

0 голосов
/ 11 февраля 2019

Я использовал gmatch с шаблоном %.*([^.]+) для итерации каждого key в предоставленных ключах.

Эта функция может быть изменена для вывода таблицы всех найденных color, нов настоящее время возвращает только одно значение.Возвращаемое значение равно color найдено или nil, если совпадений не найдено.

function FindValueFromPattern(tab, keys)
    local t_ref = tab

    for k in keys:gmatch("%.*([^.]+)") do
        if k == "<ANY>" and type(t_ref) == "table" then
            local temp1
            local temp2

            for any in pairs(t_ref) do
                local new_keys = keys:gsub(k, any)
                temp1 = FindValueFromPattern(tab, new_keys)

                new_keys = keys:gsub(k, any .. ".<ANY>")
                temp2 = FindValueFromPattern(tab, new_keys)

                if temp1 or temp2 then
                    break
                end
            end
            t_ref = temp1 or temp2
            break
        else
            if t_ref == nil or type(t_ref) ~= "table" then
                t_ref = nil
                break
            end

            t_ref = t_ref[k]
        end
    end
    return t_ref
end

Пример использования:

sample = {
    a = {
        b = {
            c = {
                color = "blue",

            },
            roloc = 1,
            color = "red",
        },
        d = {
            e = {
                color = "purple",

            },
            roloc = "wolley",
            color = "yellow",
        },
    }
}
colorFound = FindValueFromPattern(sample, "a.<ANY>.color")
if colorFound then
    print("Found: " .. colorFound )
else
    print("No matches found")
end
>> Found: red

Имейте в виду, что поведение недетерминированное, оновозможно, что результат будет yellow, а не red, и неизвестно, каким он будет до запуска кода.

0 голосов
/ 08 февраля 2019

То, что вы там делаете, крайне неэффективно.Гораздо лучшим подходом было бы разделить строку на каждом . и просто проиндексировать таблицу.

Простая версия, которая не принимает "что-либо", может выглядеть следующим образом

function findintable(tab, path)
    local pos = path:find(".", 1, true)
    if pos then
        local tab = tab[path:sub(1, pos-1)]
        if not type(tab) then error("Expected value to be table, got "..type(tab), 1) end
        return findintable(tab, path:sub(pos+1, -1))
    else
        return tab[path]
    end
end

Добавление возможности для любой клавиши (хе ... хе ... хе ...) добавит некоторую сложность и потребует цикла, но это также выполнимо

    function findintable(tab, path)
        local pos = path:find(".", 1, true)
        if not pos then
            return tab[path]
        end
        local key, rest = path:sub(1, pos-1), path:sub(pos+1, -1)
        if key == "*" then
            for k, v in pairs(tab) do
                if type(v)~="table" then return end
                local res = findintable(v, rest)
                if res then return res end
            end
            return
        else
            local tab = tab[path:sub(1, pos-1)]
            if not type(tab) then error("Expected value to be table, got "..type(tab), 1) end
            return findintable(tab, path:sub(pos+1, -1))
        end
    end

Это должно делать то, что вы хотите.Просто измените "*" на то, что вы хотите, чтобы заполнитель был.

...