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

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 ⭐

在几何区域系统的例子中,添加一个函数来实现将指定的区域旋转指定的角度

image-20230814141934874

image-20230815042015553

-- 创建一个指定边界的轴对称图形
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")
image-20230814150430157

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
]]
posted @ 2023-08-15 04:18  小能日记  阅读(158)  评论(0编辑  收藏  举报