3

I just started programming and choosed lua to write a script that processes a XML configuration file.

I load the XML file with LuaXML (C binding version) which maps it to a heavily nested table.

My problem came up when I tried to write a function that finds all matches to a tag in the xmltable. The matches are inserted in a table that is returned by the function. My problem is with the declaration of this table variable, that has to be local to function.

First I tried:

local result = result or {}

But this declares the variable with every recursion.

Finally I came up with this solution that works, but seems too complicated to me:

function findall_wrapper(xmltable, tag)

  local results = {}

  function findall(xmltable, tag)

    if xml.TAG == tag then table.insert (results, xmltable) end

    for k, v in pairs(xmltable) do
      if (type(v) == "table") then findall(v, tag) end 
    end
  end

  findall(xmltable, tag)
  return results

end

How can I solve this in a nicer, more elegant way? Why does local result = result or {} declares the variable with every recursion?

Sorry if the answer to my question is too obvious but as I mentioned, I just started programming.

4

2 に答える 2

4

ラッパー関数を使用したくないということであれば、非常に近いと思います。これはあなたが目指していたものでしたか?

function findall(xmltable, tag, results)
    local results = results or {}
    if xmltable[xml.TAG] == tag then table.insert(results, xmltable) end
    for k, v in pairs(xmltable) do
      if type(v) == "table" then findall(v, tag, results) end
    end
    return results
end
于 2013-05-07T16:12:14.073 に答える
4

実際、あなたは素敵でエレガントな解決策を思いついたと思います。あなたがしていることは、Lua の関数がクロージャーであることを悪用することです。これは、実行中にデータ構造を構築する必要がある再帰関数を記述するときに非常に役立つ手法です。完璧にするために必要なのは、 local キーワードをfunction findallinsideの前に追加することだけですfunction findall_wrapper。ヘルパー関数はローカルになり、グローバル名前空間を汚染しません。

少し詳しく説明するには:

関数には、単純な再帰関数と複雑な再帰関数の 2 種類があります。すべての再帰関数は、次の方法で実装できます。

function sum_list(l)
  if #l == 0 then
    return 0
  else
    local e = table.remove(l)
    return e + sum_list(l)
  end
end

print(sum_list({1,2,3,4}))
> 10

ここでは、中間結果を格納するためにコール スタックが使用されます。これにより、深い再帰を伴う非常に大きなスタック、または戻り値の関数への複数の呼び出しが発生する可能性があります。

それを行うためのより良い方法は、末尾再帰と呼ばれます。

function sum_list(l, a)
  if #l == 0 then
    return a
  else
    local e = table.remove(l)
    return sum_list(l, a + e)
  end
end

print(sum_list({1,2,3,4}), 0)
> 10

この例では、アキュムレータが呼び出しで渡されるため、呼び出しスタックはストレージに使用されなくなります。実装がサポートしている場合は、それを反復に変えることができます。残念ながら、すべての再帰関数が末尾再帰であるとは限りません。この場合のアキュムレータの問題は、ゼロにインスタンス化する必要があることです。そうしないと、間違った結果が得られます。

これに対する解決策は、まさにあなたがしたことです:

function sum_list(l)
  local function sum_list_helper(l, a)
    if #l == 0 then
      return a
    else
      local e = table.remove(l)
      return sum_list_helper(l, a + e)
    end
  end

  return sum_list_helper(l, 0)
end

ローカル関数が作成され、正しいインスタンス化値で呼び出されます。

于 2013-05-07T12:34:52.327 に答える