《Lua程式設計第四版》 第二部分14~17章自做練習題答案

来源:https://www.cnblogs.com/linxiaoxu/archive/2023/08/15/17632544.html
-Advertisement-
Play Games

Lua程式設計第四版第二部分編程實操自做練習題答案,帶:star:為重點。 ## 14.1 :star: > 該函數用於兩個稀疏矩陣相加 ```lua function martixAdd(a, b) local c = {} for i = 1, #a, 1 do c[i] = {} for k, ...


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))

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 博客推行版本更新,成果積累制度,已經寫過的博客還會再次更新,不斷地琢磨,高質量高數量都是要追求的,工匠精神是學習必不可少的精神。因此,大家有何建議歡迎在評論區踴躍發言,你們的支持是我最大的動力,你們敢投,我就敢肝 ...
  • > 😂 好久前寫了關於 `getStaticProps` 和 `getStaticPaths` 的內容,然而半年過去了源碼解析就一直忘記了,不久前有人提醒才想起來,補下坑。 本文主要是解讀下 `getStaticProps`、`getStaticPaths` 相關的源碼,不瞭解這兩個 `API` ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 一、需求 微信掃碼授權,如果允許授權,則登錄成功,跳轉到首頁。 二、問題 1、微信掃碼授權有幾種實現方式? 2、說一下這幾種實現方式的原理是什麼? 3、vue中的微信掃碼授權登錄,與uniapp和原生小程式的微信授權登錄,它們之間有共同點 ...
  • 本文,將向大家介紹 CSS 規範中,最新的 Anchor Positioning,翻譯為**錨點定位**。 Anchor Position 的出現,極大的豐富了 CSS 的能力,雖然語法稍顯複雜,但是有了它,能夠實現非常多之前實現起來非常困難,或者壓根無法使用純 CSS 實現的功能。 Anchor ...
  • 索引簽名是 TypeScript 中一個強大的特性,它允許我們在對象和類中使用動態的屬性名稱。通常情況下,我們會在對象或類中定義固定的屬性,但有時我們需要處理具有動態屬性名稱的情況。這時,索引簽名就派上了用場。 在這篇技術博文中,我們將介紹索引簽名的使用方法和用例,將展示如何定義帶有索引簽名的介面... ...
  • CQRS也叫命令查詢職責分離,是近年來非常流行的應用程式架構模式。本文將重點介紹如何通過MediatR的管道功能將FluentValidation集成到CQRS項目中實現驗證功能。 ...
  • 獲取byte中每一位的值 byte byData = 0x36; int n0, n1, n2, n3, n4, n5, n6, n7; n0 = (byData & 0x01) == 0x01 ? 1 : 0; n1 = (byData & 0x02) == 0x02 ? 1 : 0; n2 = ...
  • 背景:在linux環境下,很多服務我們都使用docker來跑,很是方便,容器服務獨立,配置獨立,數據獨立等等,但是有個問題,就是如果某個服務異常了,暫停了,停止了,一直重啟中,我們要怎麼及時的知道是哪個服務,併進行處理,保證業務正常運行。 本文主要介紹使用docker服務自帶的一些命令來實現一個基本 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...