12.1.2 保存带有循环的table

针对普通拓扑概念上的带有循环表和共享子表的table,我们需要另外一种不同的方法来处理。构造器不能很好地解决这种情况,我们不使用。为了表示循环我们需要将表名记录下来,下面我们的函数有两个参数:table和对应的名字。另外,我们还必须记录已经保存过的table以防止由于循环而被重复保存。我们使用一个额外的table来记录保存过的表的轨迹,这个表的下表索引为table,而值为对应的表名。

我们做一个限制:要保存的table只有一个字符串或者数字关键字。下面的这个函数序列化基本类型并返回结果。

function basicSerialize (o)

    if type(o) == "number" then

       return tostring(o)

    else       -- assume it is a string

       return string.format("%q", o)

    end

end

关键内容在接下来的这个函数,saved这个参数是上面提到的记录已经保存的表的踪迹的table

function save (name, value, saved)

    saved = saved or {}         -- initial value

    io.write(name, " = ")

    if type(value) == "number" or type(value) == "string" then

       io.write(basicSerialize(value), "\n")

    elseif type(value) == "table" then

       if saved[value] then     -- value already saved?

           -- use its previous name

           io.write(saved[value], "\n")

       else

           saved[value] = name  -- save name for next time

           io.write("{}\n")     -- create a new table

           for k,v in pairs(value) do  -- save its fields

              local fieldname = string.format("%s[%s]", name,

                                        basicSerialize(k))

              save(fieldname, v, saved)

           end

       end

    else

       error("cannot save a " .. type(value))

    end

end

举个例子:

我们将要保存的table为:

a = {x=1, y=2; {3,4,5}}

a[2] = a      -- cycle

a.z = a[1]    -- shared sub-table

调用save('a', a)之后结果为:

a = {}

a[1] = {}

a[1][1] = 3

a[1][2] = 4

a[1][3] = 5

 

a[2] = a

a["y"] = 2

a["x"] = 1

a["z"] = a[1]

(实际的顺序可能有所变化,它依赖于table遍历的顺序,不过,这个算法保证了一个新的定义中需要的前面的节点都已经被定义过)

如果我们想保存带有共享部分的表,我们可以使用同样tablesaved参数调用save函数,例如我们创建下面两个表:

a = {{"one", "two"}, 3}

b = {k = a[1]}

保存它们:

save('a', a)

save('b', b)

结果将分别包含相同部分:

a = {}

a[1] = {}

a[1][1] = "one"

a[1][2] = "two"

a[2] = 3

b = {}

b["k"] = {}

b["k"][1] = "one"

b["k"][2] = "two"

然而如果我们使用同一个saved表来调用save函数:

local t = {}

save('a', a, t)

save('b', b, t)

结果将共享相同部分:

a = {}

a[1] = {}

a[1][1] = "one"

a[1][2] = "two"

a[2] = 3

b = {}

b["k"] = a[1]

上面这种方法是Lua中常用的方法,当然也有其他一些方法可以解决问题。比如,我们可以不使用全局变量名来保存,即使用封包,用chunk构造一个local值然后返回之;通过构造一张表,每张表名与其对应的函数对应起来等。Lua给予你权力,由你决定如何实现。

 

 

 



相关链接:
lua程序设计目录 - 中国lua开发者 - lua论坛