Lua程式設計第四版第二部分編程實操自做練習題答案,帶:star:為重點。 ## 9.1 > 請編寫一個函數integral,該函數以一個函數f為參數並返回其積分的近似值 使用右矩陣法近似積分值 ```lua function integral(f) return function(a, b) lo ...
Lua程式設計第四版第二部分編程實操自做練習題答案,帶⭐為重點。
9.1
請編寫一個函數integral,該函數以一個函數f為參數並返回其積分的近似值
使用右矩陣法近似積分值
function integral(f)
return function(a, b)
local sum = 0
for i = 1, 10000, 1 do
sum = sum + f(a + (b - a) * i / 10000)
end
return sum * (b - a) / 10000
end
end
function x3(x)
return 2 * x + 3 * x ^ 3
end
jf = integral(x3)
print(jf(0, 10)) -- 7601.510075 近似 7600
9.2
如下代碼段將輸出什麼結果
function F(x)
return {
set = function(y)
x = y
end,
get = function()
return x
end
}
end
o1 = F(10)
o2 = F(20)
print(o1.get(), o2.get())
o2.set(100)
o1.set(300)
print(o1.get(), o2.get())
-- 10 20
-- 300 100
9.3 ⭐
編寫練習5.4的柯里化版本
柯里化(Currying)是把接受多個參數
的函數變換成接受一個單一參數
(最初函數的第一個參數)的函數
,並且返回接受餘下的參數且返回結果
的新函數的技術。
function newpoly(t)
return function(x)
local sum = 0
for i, v in ipairs(t) do
sum = sum + v * x ^ (i - 1)
end
return sum
end
end
t = newpoly({3, 0, 1})
print(t(0))
print(t(5))
print(t(10))
9.4
使用幾何區域系統的例子,繪製一個北半球所能看到的峨眉月
編寫一個被其他函數B包含的函數A時,被包含的函數A可以訪問包含其的函數B的所有局部變數,這種特性被稱為詞法定界
-- 利用高階函數和詞法定界,定義一個指定圓心和半徑創建圓盤的工廠 --
function disk(cx, cy, r)
return function(x, y)
return (x - cx) ^ 2 + (y - cy) ^ 2 <= r ^ 2
end
end
-- 創建一個指定邊界的軸對稱圖形
function rect(left, right, top, bottom)
return function(x, y)
return left <= x and x <= right and bottom <= y and y <= top
end
end
-- 創建任何區域的補集
function complement(r)
return function(x, y)
return not r(x, y)
end
end
-- 創建任何區域的並集
function union(r1, r2)
return function(x, y)
return r1(x, y) or r2(x, y)
end
end
-- 交集
function intersection(r1, r2)
return function(x, y)
return r1(x, y) and r2(x, y)
end
end
-- 差集
function difference(r1, r2)
return function(x, y)
return r1(x, y) and not r2(x, y)
end
end
-- 按指定增量平移區域
function translate(r, dx, dy)
return function(x, y)
return r(x - dx, y - dy)
end
end
function 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
circle = disk(0, 0, 0.5)
circle2 = translate(circle, -0.3, 0.2)
shape = difference(circle, circle2)
plot(shape, 512, 512, "test.pbm")
9.5 ⭐
在幾何區域系統的例子中,添加一個函數來實現將指定的區域旋轉指定的角度
-- 創建一個指定邊界的軸對稱圖形
function rect(left, right, top, bottom)
return function(x, y)
return left <= x and x <= right and bottom <= y and y <= top
end
end
-- 創建任何區域的並集
function union(r1, r2)
return function(x, y)
return r1(x, y) or r2(x, y)
end
end
-- 圖形根據坐標軸原點順時針旋轉d弧度
function rotate(r, d)
return function(x, y)
return r(-math.cos(d) * x + y * math.sin(d), -math.sin(d) * x - math.cos(d) * y)
end
end
function 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
shape1 = rect(-0.5, 0.5, 0.5, -0.5)
shape2 = rect(-0.25, 0.25, 0.75, -0.25)
shape1 = rotate(shape, math.pi * 0.1) -- s1所處的坐標系平面被旋轉了,s2的並沒有
shape = union(shape1, shape2)
plot(shape, 512, 512, "test.pbm")
10.1
請編寫一個函數split,該函數接收兩個參數,第1個參數是字元串,第2個參數是分隔符模式,函數的返回值是分隔符分割後的原始字元串中每一部分的序列
function split(s, sep)
t = {}
-- tmpindex = 0
-- index = 1
-- index = string.find(s, sep, index, true)
-- while index do
-- table.insert(t, string.sub(s, tmpindex + 1, index))
-- tmpindex = index
-- index = string.find(s, sep, index + 1, true)
-- end
-- if index ~= #s then
-- table.insert(t, string.sub(s, tmpindex + 1, #s))
-- end
for w in string.gmatch(s, "[^" .. sep .. "]+") do
t[#t + 1] = w
end
return t
end
t = split("a whole new world", " ")
-- t = split("", " ")
for k, v in pairs(t) do
print(k, v)
end
10.2 ⭐
模式 [^%d%u] 與 [%D%U] 是否等價
不一樣
[^%d%u] 是 (數字∪大寫字母)的補集 = 排除數字和大寫字母
> string.find("123ABCA","[^%d%u]")
nil
> string.find("123ABCa","[^%d%u]")
7 7
[%D%U] 是 數字的補集∪大寫字母的補集 = 全集
> string.find("123ABC","[%D%U]")
1 1
使用第一種是正確的
10.3
請編寫一個函數transliterate,該函數接收兩個參數,第1個參數是字元串,第2個參數是一個表。函數transliterate根據第2個參數中的表使用一個字元替換字元串中的字元。如果表中將a映射為b,那麼該函數則將所有a替換為b。如果表中將a映射為false,那麼該函數則把結果中的所有a移除
function transliterate(s, t)
return string.gsub(s, ".", function(w)
if t[w] == false then
return ""
else
return t[w]
end
end)
end
s = "apple"
s = transliterate(s, {
a = "b",
p = false,
l = "i",
e = "g"
})
print(s)
10.4 ⭐
我們定義了一個trim函數。
- 構造一個可能會導致trim耗費O(n^2)時間複雜度的字元串
- 重寫這個函數使得其時間複雜度為O(n)
function trim1(s)
return string.gsub(s, "^%s*(.-)%s*$", "%1")
end
-- "雙指針法實現"
function trim2(s)
local top = string.find(s, "%S")
local tail = #s
for i = 1, #s, 1 do
if string.find(string.sub(s, -i, -i), "%s") then
tail = #s - i
else
break
end
end
return string.sub(s, top, tail)
end
s = string.rep(" ", 2 ^ 13)
s = (" " .. "a" .. s .. "a" .. " ")
start = os.clock()
t = trim2(s)
print(t:len() == 2 ^ 13 + 2)
print(os.clock() - start)
10.5
請使用轉義序列\x編寫一個函數,將一個二進位字元串格式化為Lua語言中的字元串常量
-- 二進位字元串格式化為Lua語言中的字元串常量
function escape(s)
local pattern = string.rep("[01]", 8)
s = string.gsub(s, pattern, function(bits)
return string.format("\\x%02X", tonumber(bits, 2))
end)
return s
end
print(escape( "0100100001100001011100000111000001111001001000000110010101110110011001010111001001111001011001000110000101111001"))
-- \x48\x61\x70\x70\x79\x20\x65\x76\x65\x72\x79\x64\x61\x79
10.6
請為UTF-8字元串重寫10.3
function transliterate(s, t)
return string.gsub(s, utf8.charpattern, function(w)
if t[w] == false then
return ""
else
return t[w]
end
end)
end
s = "天氣真好"
s = transliterate(s, {
["天"] = "大",
["氣"] = false,
["真"] = "爺"
})
print(s)
10.7
請編寫一個函數,該函數用於逆轉一個UTF-8字元串
function reverseUTF8(s)
t = {}
for w in string.gmatch(s, utf8.charpattern) do
table.insert(t, 1, w)
end
return table.concat(t)
end
print(reverseUTF8("天氣真好"))
ANTI SPIDER BOT -- www.cnblogs.com/linxiaoxu
11.1
請改寫該程式,使它忽略長度小於4個字母的單詞
local counter = {}
io.input(arg[2] or "test.txt")
for line in io.lines() do
-- 不能放在這,相當於無限執行gmatch死迴圈,gmatch只執行一次獲取迭代器
for word in string.gmatch(line, "%w+") do
if #word < 4 then
goto a
end
counter[word] = (counter[word] or 0) + 1
::a:: -- 跳過當前計數
end
end
local words = {}
for k, _ in pairs(counter) do
words[#words + 1] = k
end
table.sort(words, function(w1, w2)
return counter[w1] > counter[w2] or counter[w1] == counter[w2] and w1 < w2
end)
local n = math.min(tonumber(arg[1]) or math.huge, #words)
for i = 1, n, 1 do
print(i, words[i], counter[words[i]])
end
11.2 ⭐
該程式還能從一個文本文件中讀取要忽略的單詞列表
local counter = {}
local ignore = {}
io.input(arg[2] or "test.txt")
f = io.open(arg[3] or "ignore.txt", 'r')
for line in f:lines() do
ignore[string.match(line, "%w+")] = true
end
for line in io.lines() do
-- 不能放在這,相當於無限執行gmatch死迴圈,gmatch只執行一次獲取迭代器
for word in string.gmatch(line, "%w+") do
if #word < 4 or ignore[word] then
goto a
end
counter[word] = (counter[word] or 0) + 1
::a:: -- 跳過當前計數
end
end
local words = {}
for k, _ in pairs(counter) do
words[#words + 1] = k
end
table.sort(words, function(w1, w2)
return counter[w1] > counter[w2] or counter[w1] == counter[w2] and w1 < w2
end)
local n = math.min(tonumber(arg[1]) or math.huge, #words)
for i = 1, n, 1 do
print(i, words[i], counter[words[i]])
end
12.1
該函數返回指定日期和時間後恰好一個月的日期和時間(按數字形式表示)
function oneMonthLater(t)
t.month = t.month + 1
return os.time(t)
end
print(oneMonthLater(os.date("*t")))
12.2
該函數返回指定日期是星期幾(用整數表示,1表示星期天
function wday(t)
return t.wday
end
print(wday(os.date("*t")))
12.3
該函數的參數為一個日期和時間(數值),返回當天中已經經過的秒數
function todayPassSeconds(t)
t = os.date("*t", t)
return t.hour * 3600 + t.min * 60 + t.sec
end
print(todayPassSeconds(os.time()))
12.4
該函數的參數為年,返回該年中第一個星期五是第幾天
function findFirstFriday(y)
t = os.date("*t")
t.year = y
t.month = 1
for i = 1, 7, 1 do
t.day = i
t = os.date("*t", os.time(t))
if t.yday == 6 then
return t.yday
end
end
end
print(findFirstFriday(2023))
12.5
該函數用於計算兩個指定日期之間相差的天數
function timeDiffDays(x, y)
seconds = os.time(x) - os.time(y)
return seconds // (24 * 3600)
end
t = os.date("*t")
t.year = 2012
print(timeDiffDays(os.date("*t"), t))
12.6
該函數用於計算兩個指定日期之間相差的月份
function timeDiffMonth(x, y)
year = x.year - y.year
month = x.month - y.month
return math.abs(year) * 12 + math.abs(month)
end
t = os.date("*t")
t.year = 2023
t.month = 7
print(timeDiffMonth(os.date("*t"), t))
12.7
向指定日期添加一個月再添加一天得到的結果,是否與先添加一天再添加一個月得到的結果相同
buff = {
year = 2023,
month = 6,
day = 30
}
t = os.date("*t", os.time(buff))
t.day = t.day + 1
t.month = t.month + 1
t2 = os.date("*t", os.time(buff))
t2.month = t2.month + 1
t2.day = t2.day + 1
print(os.difftime(os.time(t), os.time(t2)))
t = os.date("*t", os.time(buff))
t.day = t.day + 1
t = os.date("*t", os.time(t))
t.month = t.month + 1
t2 = os.date("*t", os.time(buff))
t2.month = t2.month + 1
t2 = os.date("*t", os.time(t2))
t2.day = t2.day + 1
print(os.difftime(os.time(t), os.time(t2)))
[[
0.0
86400.0 第二種方法會產生不同結果
]]
12.8
該函數用於輸出操作系統的時區
時區劃分是規定將全球劃分為24個時區,東、西各12個時區。1884年在華盛頓召開的一次國際經度會議(又稱國際子午線會議)上,規定將全球劃分為24個時區(東、西各12個時區)。規定英國(格林尼治天文臺舊址)為中時區(零時區)、東112區,西112區。每個時區橫跨經度15度,時間正好是1小時。最後的東、西第12區各跨經度7.5度,以東、西經180度為界。
function timeZone()
local now = os.time()
local glnz = os.time(os.date("!*t")) -- 格林尼治時間
return (now - glnz) // 3600
end
print(timeZone())
-- 8 東8區
13.1 ⭐
該函數用於進行無符號整型數的取模運算
將無符號整數最低位提取充當b(0或1),其餘部分為a,a必為偶數。a再拆分為 2 * a/2,2為j,a/2為k。
\[(a+b) \mod c = ((a \mod c) + (b \mod c)) \mod c \\ (j*k) \mod c = ((j \mod c) * (k \mod c)) \mod c \]--設條件 d<= 2^64 -1 , c <= 2^63-1
function unsignedMod(d, c)
local even = d & -2
local odd = d & 1
local res1 = ((2 % c) * ((even >> 1) % c)) % c -- 等價於 even % c
local res2 = (res1 + (odd % c)) % c
return res2
end
print(unsignedMod(-101, 4294967296) == 4294967195)
print(unsignedMod(-1, math.maxinteger) == 1)
print(unsignedMod(-1111111, 114514) == 59155)
13.2
實現計算Lua語言中整型數所占位數的不同方法
-- 忽略符號位,計算剩下的63位中共占用幾位
function countBits1(d)
local count = 0
while d ~= 0 do
d = d // 2
count = count + 1
end
return count
end
function countBits2(d)
local count = 0
d = d & math.maxinteger
while d ~= 0 do
d = d >> 1
count = count + 1
end
return count
end
print(countBits1(math.maxinteger) == 63)
print(countBits1(10) == 4)
print(countBits1(1) == 1)
print(countBits1(0) == 0)
print(countBits2(math.maxinteger) == 63)
print(countBits2(10) == 4)
print(countBits2(1) == 1)
print(countBits2(0) == 0)
13.3
如何判斷一個指定整數是不是2的整數次冪
-- 忽略符號位,計算剩下的63位中共占用幾位
function isPowerOf2(d)
if d <= 0 then
return false
end
while d & 1 == 0 do
d = d >> 1
end
d = d >> 1
if d > 0 then
return false
else
return true
end
end
print(isPowerOf2(1))
print(isPowerOf2(2))
print(isPowerOf2(3))
print(isPowerOf2(4))
print(isPowerOf2(5))
print(isPowerOf2(6))
print(isPowerOf2(7))
print(isPowerOf2(8))
13.4
該函數用於計算指定整數的漢明權重(數二進位表示的1的個數)
function hw(d)
local count = 0
while d ~= 0 do
if d & 1 == 1 then
count = count + 1
end
d = d >> 1
end
return count
end
print(hw(1)) -- 1
print(hw(2)) -- 1
print(hw(3)) -- 2
print(hw(4)) -- 1
print(hw(5)) -- 2
print(hw(6)) -- 2
print(hw(7)) -- 3
print(hw(8)) -- 1
print(hw(-1)) -- FFFF 64
print(hw(-2)) -- FFFE 63
print(hw(-3))
print(hw(-4))
print(hw(-5))
print(hw(-6))
print(hw(-7))
print(hw(-8))
13.5
該函數用於判斷註定整數的二進位表示是否為迴文數
function isPalindrome(d)
local t = {}
while d ~= 0 do
table.insert(t, d & 1)
d = d >> 1
end
for i = 1, 32, 1 do
if t[i] ~= t[64 - i + 1] then
return false
end
end
return true
end
print(isPalindrome(1))
print(isPalindrome(0))
print(isPalindrome(-1))
print(isPalindrome(math.mininteger + 1))
13.6 ⭐
請在Lua語言實現一個比特數組
- newBiteArrary(n) 創建一個具有n個比特的數組
- setBit(a, n, v) 將布爾值v賦值給數組a的第n位
- testBit(a, n) 將第n位的值作為布爾值返回
function newBiteArrary(n)
local t = {}
local count = (n - 1) // 64 + 1
for i = 1, n, 1 do
table.insert(t, 0)
end
return t
end
function setBit(a, n, v)
local count = (n - 1) // 64 + 1
local n = (n - 1) % 64 + 1
local tmp1 = math.mininteger >> (n - 1) -- 000001000000
local tmp2 = ~tmp1 -- 1111101111111
if v then
a[count] = a[count] | tmp1
else
a[count] = a[count] & tmp2
end
end
function testBit(a, n)
local count = (n - 1) // 64 + 1
local n = (n - 1) % 64 + 1
local tmp1 = math.mininteger >> (n - 1) -- 000001000000
if tmp1 & a[count] == 0 then
return false
else
return true
end
end
a = newBiteArrary(300)
setBit(a, 1, true)
setBit(a, 100, true)
setBit(a, 105, true)
setBit(a, 105, false)
setBit(a, 300, true)
print(testBit(a, 1))
print(testBit(a, 100))
print(testBit(a, 105))
print(testBit(a, 300))
13.7 ⭐
假設有一個以一系列記錄組成的二進位文件,其中的每一個記錄的格式為
struct Record{ int x; char[3] code; float value; };
請編寫一個程式,該程式讀取這個文件,然後輸出value欄位的總和
function saveRecords(t)
local f = io.open("records.txt", "w")
for _, v in ipairs(t) do
f:write(string.pack("j", v.x))
f:write(string.pack("z", v.code))
f:write(string.pack("n", v.value))
end
f:close()
end
function readCalcuteValue()
local f = io.open("records.txt", "rb")
local s = f:read('a')
f:close()
local i = 1
local total = 0
while i <= #s do
local x, code, value
x, i = string.unpack('j', s, i)
code, i = string.unpack('z', s, i)
value, i = string.unpack('n', s, i)
print(x, code, value)
total = total + value
end
return total
end
t = {{
x = 100000,
code = "abc",
value = 100
}, {
x = 200000,
code = "def",
value = 200
}, {
x = 300000,
code = "ghi",
value = 300
}, {
x = 400000,
code = "jkl",
value = 400
}}
saveRecords(t)
sum = readCalcuteValue()
print(sum)
[[
100000 abc 100.0
200000 def 200.0
300000 ghi 300.0
400000 jkl 400.0
1000.0
]]