首页 > 编程语言 >《Lua程序设计第四版》 第二部分14~17章自做练习题答案

《Lua程序设计第四版》 第二部分14~17章自做练习题答案

时间:2023-08-15 21:59:59浏览次数:43  
标签:练习题 end 14 17 graph write io return local

Lua程序设计第四版第二部分编程实操自做练习题答案,带⭐为重点。

14.1 ⭐

该函数用于两个稀疏矩阵相加

function martixAdd(a, b)
    local c = {}
    for i = 1, #a, 1 do
        c[i] = {}
        for k, v in pairs(a[i]) do
            c[i][k] = v
        end
    end
    for i = 1, #b, 1 do
        for k, v in pairs(b[i]) do
            c[i][k] = (c[i][k] or 0) + v
            c[i][k] = (c[i][k] ~= 0) and c[i][k] or nil
        end
    end
    return c
end

A = {{
    [5] = 1
}, {}, {
    [1] = 3,
    [3] = 4
}, {}, {
    [4] = -1
}}

B = {{
    [2] = 2
}, {}, {
    [1] = -3,
    [4] = -3
}, {
    [3] = 8
}, {
    [1] = 7
}}

for k, v in pairs(martixAdd(A, B)) do
    for j, i in pairs(v) do
        print(k .. "行", j .. "列", i)
    end
end

14.2

改写队列的实现,使得当队列为空时两个索引都返回0

function listNew()
    return {
        _first = 0,
        _last = -1,
        first = function()
            if _first > _last then
                return 0
            end
            return _first
        end,
        last = function()
            if _first > _last then
                return 0
            end
            return _last
        end
    }
end

l = listNew()
-- 模拟空队列
l._first = 10
l._last = 9
print(l.first, l.last)

14.3

修改图所用的数据结构,使得图可以保存每条边的标签。该数据结构应该使用包括两个字段的对象来表示每一条边,即边的标签和边指向的节点。与邻接集合不同,每一个节点保存的是从当前节点出发的边的集合。

local function name2node(graph, name)
    local node = graph[name]
    if not node then
        graph[name] = {
            name = name,
            edges = {}
        }
        node = graph[name]
    end
    return node
end

function readgraph(file)
    local graph = {}
    f = io.open(file, 'r')
    for l in f:lines() do
        local name1, name2, edgeLabel = string.match(l, "(%S+)%s+(%S+)%s+(%S+)")
        local from = name2node(graph, name1)
        local to = name2node(graph, name2)
        table.insert(from.edges, {
            ["label"] = edgeLabel,
            ["adj"] = to
        })
    end
    return graph
end

graph = readgraph("graph.txt")
for k, v in pairs(graph) do
    for _, v in pairs(v.edges) do
        print(k, v.label, v.adj.name)
    end
end

[[
C       1       D
C       3       E
D       1       C
D       7       E
A       4       B
A       2       D
B       1       D
B       4       C
]]
A B 4
A D 2
B D 1
D C 1
C D 1
B C 4
D E 7
C E 3

14.4 ⭐

使用14.3的表示方式,其中边的标签代表两个终端节点之间的距离。该函数使用Dijkstra算法寻找两个指定节点之间的最短路径。

image-20230815163212132
local function name2node(graph, name)
    local node = graph[name]
    if not node then
        graph[name] = {
            name = name,
            edges = {}
        }
        node = graph[name]
    end
    return node
end

function readgraph(file)
    local graph = {}
    f = io.open(file, 'r')
    for l in f:lines() do
        local name1, name2, edgeLabel = string.match(l, "(%S+)%s+(%S+)%s+(%S+)")
        local from = name2node(graph, name1)
        local to = name2node(graph, name2)
        table.insert(from.edges, {
            ["label"] = edgeLabel,
            ["adj"] = to
        })
    end
    return graph
end

graph = readgraph("graph.txt")
for k, v in pairs(graph) do
    for _, v in pairs(v.edges) do
        print(k, v.label, v.adj.name)
    end
end

function Dijkstra(graph, a, b)
    -- 点不存在
    if not graph[a] or not graph[b] then
        return nil
    end
    local D = {}
    local T = {}
    -- D 加入起点
    D[graph[a]] = 0
    -- T 加入其他点,初始化可达最短距离为正无穷大
    for name, node in pairs(graph) do
        if name ~= a then
            T[node] = math.maxinteger
        end
    end
    -- 当D中不包含b点时循环
    while not D[graph[b]] do
        -- 根据 D 迭代 T 可达最短距离
        for node, d in pairs(D) do
            for _, edge in pairs(node.edges) do
                if not D[edge.adj] then
                    local newD = d + tonumber(edge.label)
                    T[edge.adj] = math.min(T[edge.adj], newD)
                end
            end
        end
        -- 选择 T 可达距离最小的点
        local tmp = nil
        for node, d in pairs(T) do
            tmp = tmp or node
            if d < T[tmp] then
                tmp = node
            end
        end
        -- 如果没有点被选中,退出循环
        if T[tmp] == math.maxinteger then
            return nil
        end
        -- 将前面选择到的点加入D,并从T删除
        D[tmp] = T[tmp]
        T[tmp] = nil
    end
    return D[graph[b]]
end

d = Dijkstra(graph, "A", "E")
if not d then
    print("无路径")
end
print(d)

-- 具体路径计算为 A->E 路径距离 - (X->E 路径距离) == A -> X 路径距离
-- 即 A->X->E

15.1~15.2

修改序列化函数,使其带缩进地输出嵌套表,并添加["key"]=value的语法

function serialize(o, tab)
    tab = tab or 1
    local t = type(o)
    if t == "number" or t == "string" or t == "boolean" or t == "nil" then
        io.write(string.format("%q", o))
    elseif t == "table" then
        io.write("{\n")
        for k, v in pairs(o) do
            io.write(string.rep("   ", tab))
            io.write(" [", string.format("%s", serialize(k)), "] = ")
            serialize(v, tab + 1)
            io.write(",\n")
        end
        io.write(string.rep("   ", tab))
        io.write("}")
    else
        error("cannot serialize a" .. t)
    end
end

A = {
    B = {
        E = "dog",
        H = {
            F = "PIG",
            G = "goose"
        }
    },
    C = "apple",
    D = 114514,
    I = {1, 2, 3, {4, 5, 6}}
}

serialize(A)

[[
{
    ["C"] = "apple",
    ["D"] = 114514,
    ["I"] = {
       [1] = 1,
       [2] = 2,
       [3] = 3,
       [4] = {
          [1] = 4,
          [2] = 5,
          [3] = 6,
         },
      },
    ["B"] = {
       ["E"] = "dog",
       ["H"] = {
          ["G"] = "goose",
          ["F"] = "PIG",
         },
      },
   }
]]

15.3

修改15.2,使其只在必要时,当键为字符串而不是合法标识符时才使用形如["key"]=value的语法

function serialize(o, tab)
    tab = tab or 1
    local t = type(o)
    if t == "number" or t == "string" or t == "boolean" or t == "nil" then
        io.write(string.format("%q", o))
    elseif t == "table" then
        io.write("{\n")
        for k, v in pairs(o) do
            io.write(string.rep("   ", tab))
            if type(k) == "string" and string.match(k, "[%a_]+[%w_]*") then
                io.write("  ", k, "  = ")
            else
                io.write(" [")
                serialize(k)
                io.write("] = ")
            end
            serialize(v, tab + 1)
            io.write(",\n")
        end
        io.write(string.rep("   ", tab))
        io.write("}")
    else
        error("cannot serialize a" .. t)
    end
end

A = {
    B = {
        E = "dog",
        H = {
            F = "PIG",
            G = "goose"
        }
    },
    C = "apple",
    D = 114514,
    I = {1, 2, 3, {4, 5, 6}}
}

serialize(A)

[[
{
     C  = "apple",
     D  = 114514,
     I  = {
       [1] = 1,
       [2] = 2,
       [3] = 3,
       [4] = {
          [1] = 4,
          [2] = 5,
          [3] = 6,
         },
      },
     B  = {
        E  = "dog",
        H  = {
           G  = "goose",
           F  = "PIG",
         },
      },
   }
]]

15.4

使其在可能时使用列表的构造器语法。例如应将表{14,15,19}序列化为{14,15,19},而不是{[1] =14,[2]=15,[3]=19},在遍历该部分的时候不要重复序列化。

function serialize(o)
    local typ = type(o)
    if typ == "number" or typ == "string" or typ == "boolean" or typ == "nil" then
        io.write(string.format("%q", o))
    elseif typ == "table" then
        io.write("{\n")
        local hash = {}
        -- 遍历列表并进行hash记录
        for i, v in ipairs(o) do
            serialize(v)
            io.write(",")
            hash[i] = true
        end
        _ = (#hash ~= 0) and io.write("\n") or nil
        -- 遍历除列表之外的所有元素
        for k, v in pairs(o) do
            if type(k) == "string" and string.match(k, "[%a_]+[%w_]*") then
                io.write(" ", k, " = ")
            elseif hash[k] == true then
                goto continue
                -- do nothing
            else
                io.write(" [")
                serialize(k)
                io.write("] = ")
            end
            serialize(v)
            io.write(",\n")
            ::continue::
        end
        io.write("}")
    else
        error("cannot serialize a" .. t)
    end
end

A = {
    B = {
        E = "dog",
        H = {
            F = "PIG",
            G = "goose"
        }
    },
    C = "apple",
    D = 114514,
    I = {1, 2, 3, {4, 5, 6}},
    [1] = 4,
    [2] = 3,
    [3] = 3,
    [5] = 4
}

serialize(A)

TEST = {
    4,
    3,
    3,
    [5] = 4,
    C = "apple",
    D = 114514,
    I = {1, 2, 3, {4, 5, 6}},
    B = {
        E = "dog",
        H = {
            G = "goose",
            F = "PIG"
        }
    }
}

[[
{
4,3,3,
 [5] = 4,
 C = "apple",
 D = 114514,
 I = {
1,2,3,{
4,5,6,
},
},
 B = {
 E = "dog",
 H = {
 G = "goose",
 F = "PIG",
},
},
}
]]

16.1

通常,在加载代码段时增加一些前缀很有用。该函数类似于函数load,不过会将第一个参数增加到待加载的代码段之前。

像原始的load函数一样,函数loadwithprefix应该既可以接收字符串形式的代码段,也可以通过函数进行读取。即使待加载的代码段是字符串形式的,函数loadwithprefix也不应该进行实际的字符串连接操作。相反,它应该调用函数load并传入一个恰当的读取函数来实现功能,这个读取函数首先返回要增加的代码,然后返回原始的代码段。

function loadWithPrefix(s, f)
    local tmp = 1
    return load(function()
        if tmp == 1 then
            tmp = 2
            return s
        elseif tmp == 2 then
            if type(f) == "string" then
                tmp = 3
                return f
            elseif type(f) == "function" then
                return f()
            end
        else
            return nil
        end
    end)
end

f = loadWithPrefix("local x = 100;", "print(x)")
f()

f = loadWithPrefix("local x = 100;", io.lines('load.txt', '*L'))
f()

16.2 ⭐

请编写一个函数mulitiload,该函数接收一组字符串或函数来生成函数。其他规则与16.1类似。

function multiLoad(...)
    local t = {...}
    local i = 1
    return load(function()
        ::continue::
        if i <= #t then
            local v = t[i]
            if type(v) == "string" then
                i = i + 1
                return v
            elseif type(v) == "function" then
                local tmp = v()
                if tmp then
                    return tmp
                else
                    i = i + 1
                    goto continue -- 进行下一回合
                end
            end
        else
            return nil
        end
    end)
end

f = multiLoad("local x = 100;", "print(x)", "x = 10; print(x);")
f()

f = multiLoad("local x = 100;", io.lines('load.txt', '*L'), "x = 10; print(x);")
f()

16.3 ⭐

该函数对于指定的n返回特定版本的函数stringrep_n。在实现方面不能使用闭包,而是应该构造出包含合理指令序列的Lua代码,然后再使用函数load生成最终的函数。请比较通用版本的stringrep函数与我们自己实现的版本之间的性能差异

自己实现的版本重复次数是固定的,省去了计算各组合次数的性能开销,速度更快。

function stringrepN(n)
    local top = [[local s = ...;local r = "";]]
    local middle = ""
    local tail = "r=r..s;return r;"
    local count = 0
    local tmp = n
    while n // 2 ~= 0 do
        count = count + 1
        n = n // 2
        middle = middle .. 's=s..s;'
    end
    for i = 1, tmp - 2 ^ count, 1 do
        top = top .. 'r=r..s;'
    end
    return load(top .. middle .. tail)
end

stringrep = stringrepN(256)

start = os.clock()
stringrep("abcdefg", 2 ^ 25)
print(os.clock() - start)

start = os.clock()
string.rep("abcdefg", 2 ^ 25)
print(os.clock() - start)

print(string.rep("a", 256) == stringrep("a"))

[[
0.0
0.321
true
]]

16.4

你能想到一个使pcall(pcall,f)的第1个返回值为false的f?

function b(x)
    print(x)
end

-- 一个函数和要传入的参数
ok, msg = pcall(b, 100)
print(ok, msg)
io.write("\n")
ok, msg = pcall(nil) -- 有错误信息但未引发错误
print(ok, msg)
io.write("\n")
ok, msg = pcall(pcall, nil) -- 错误信息返回了false
print(ok, msg)
io.write("\n")
ok, msg = pcall(pcall, (function()
end)()) -- 连nil也不返回的空value
print(ok, msg)
io.write("\n")

17.2

将几何区域系统的实现,9.4重写为恰当的模块

mod = require 'mod'
circle = mod.disk(0, 0, 0.5)
circle2 = mod.translate(circle, -0.3, 0.2)
shape = mod.difference(circle, circle2)
local plot = mod.plot
plot(shape, 512, 512, "test.pbm")
M = {}

function M.disk(cx, cy, r)
    return function(x, y)
        return (x - cx) ^ 2 + (y - cy) ^ 2 <= r ^ 2
    end
end

function M.rect(left, right, top, bottom)
    return function(x, y)
        return left <= x and x <= right and bottom <= y and y <= top
    end
end

function M.complement(r)
    return function(x, y)
        return not r(x, y)
    end
end

function M.union(r1, r2)
    return function(x, y)
        return r1(x, y) or r2(x, y)
    end
end

function M.intersection(r1, r2)
    return function(x, y)
        return r1(x, y) and r2(x, y)
    end
end

function M.difference(r1, r2)
    return function(x, y)
        return r1(x, y) and not r2(x, y)
    end
end

function M.translate(r, dx, dy)
    return function(x, y)
        return r(x - dx, y - dy)
    end
end

function M.plot(r, M, N, file)
    f = io.open(file, "w")
    f:write("P1\n", M, " ", N, "\n")
    for i = 1, N, 1 do
        local y = (N - i * 2) / N
        for j = 1, M do
            local x = (j * 2 - M) / M
            f:write(r(x, y) and "1" or "0")
        end
        f:write("\n")
    end
    f:close()
end

return M

17.3

如果在lua的搜索路径中添加一项绝对路径的文件名,会导致require一个不存在的模块时,这个模块都会搜索到固定文件名的文件作为它导入的模块
而且模块名不同,在package.loaded都会缓存,加载相同固定路径的module,在内存中也是两份独立的副本
在极端情况下有用,相当于始终会有默认的模块被加载,可以用这套机制来拓展require函数
https://www.lua.org/pil/8.1.html

17.4

编写一个同时搜索lua文件和C标准库的搜索器

function searcher(file, path)
    if type(file) ~= "string" or type(path) ~= "string" then
        return nil, error("not string type")
    end
    file = package.searchpath(file, path)
    if not file then
        return nil, error("no file")
    end
    f = loadfile(file)
    if not f then
        f = package.loadlib(file)
    end
    if not f then
        return nil, error("failed load")
    end
    return f(), nil
end

local m = searcher("mod", "./?.lua;./?.so;/usr/lib/lua5.2/?.so;/usr/share/lua5.2/?.lua")
print(m.rect(1, 1, 1, 1))

标签:练习题,end,14,17,graph,write,io,return,local
From: https://www.cnblogs.com/linxiaoxu/p/17632544.html

相关文章

  • ABC314 E和CF892 Div2D-E
    ABC314EE-Roulettes(atcoder.jp)大致意思是给你n个轮盘,第i个轮盘等概率的p[i]个点数,玩一次c[i]价钱,问要达到m点的最小期望花费是多少,每次可以任意选一个。乍一看很像背包,偏了方向,所以当时没有做出来。也考虑过其它的DP,关键是0怎么处理没搞明白所以赛后看他人的代码和题解......
  • 20230814 总结
    T1简单题(simple)题目大意:给定联通无向图,求满足以下条件的边数量:每条边最多在一个简单环内(也就是环,当时愣了很久,于是就没打出来)对于任意编号为\(i,j(i<j)\)的两点,存在一条它们之间的简单路径上面有\(j-i+1\)个点首先我们可以发现,条件2很好求,就是肯定有一条从1到n的链......
  • 14 观察者模式 -- go语言设计模式
    观察者模式也叫做发布-订阅模式。观察者通过通知器(发行商)把自己注册到(订阅)特定的通知(杂志)。当有通知的时候,观察者只从通知器得到它订阅的通知。观察者模式的实现代码packagemainimport"fmt"//---------抽象层--------//抽象的观察者typeListenerinterface{ OnTe......
  • [POI2014] PAN-Solar Panels
    区间\(\left(l,r\right]\)中存在\(n\)的倍数的充要条件是\(\left\lfloor\frac{r}{n}\right\rfloor>\left\lfloor\frac{l}{n}\right\rfloor\)。证明:记有整数\(k\)满足\(k\timesn\in\left(l,r\right]\)。那么有\[\displaystylel<k\timesn......
  • HDU 5014
    NumberSequenceTimeLimit:4000/2000MS(Java/Others)    MemoryLimit:65536/65536K(Java/Others)TotalSubmission(s):2075    AcceptedSubmission(s):814SpecialJudgeProblemDescriptionThereisaspecialnumbersequencewhichhasn+1inte......
  • HDU 1423
    GreatestCommonIncreasingSubsequenceTimeLimit:2000/1000MS(Java/Others)  MemoryLimit:65536/32768K(Java/Others)TotalSubmission(s):5384  AcceptedSubmission(s):1742ProblemDescriptionThisisaproblemfromZOJ2432.Tomakeiteasyer,you......
  • 漏洞复现报告:CVE-2017-7103 JQuery框架XSS漏洞
    1.简介jQuery是一个快速、简洁的JavaScript框架,是一个丰富的JavaScript代码库。jQuery设计的目的是为了写更少的代码,做更多的事情。它封装JavaScript常用的功能代码,提供一种简便的JavaScript设计模式,优化HTML文档操作、事件处理、动画设计和Ajax交互。据一项调查报告,在对4......
  • Hugging News #0814: Llama 2 学习资源大汇总
    每一周,我们的同事都会向社区的成员们发布一些关于HuggingFace相关的更新,包括我们的产品和平台更新、社区活动、学习资源和内容更新、开源库和模型更新等,我们将其称之为「HuggingNews」。本期HuggingNews有哪些有趣的消息,快来看看吧!......
  • 8-15| _ctypes.COMError: (-2147352567, '发生意外。', ('无法获取 Document 对象', '
    此错误是一个COM错误,它与试图从Python通过`pyautocad`与AutoCAD通信时出现的问题有关。错误信息"无法获取Document对象"指示了问题的本质,即Python无法访问AutoCAD的当前文档。这里有一些建议来解决这个问题:1.**确保AutoCAD已经运行**:在尝试从Python访问Aut......
  • P3514
    题目传送门这是一个稍微复杂一点且较容易想到的做法。考虑特殊到一般,我们固定一个右端点\(l\),枚举左端点\(r\),将\([l,r]\)算入答案。这样显然会漏掉一些答案,发现必然是进行\(+2\)时跳过了一个数,考虑如何得到这个数。发现如果\(a_l=1\),我们将\(l\leftarrowl+1\),则枚举......