《Lua程序设计第四版》 第一部分自做练习题答案
Lua程序设计第四版第一部分语言基础自做练习题答案,带⭐为重点。
1.1
输入负数,程序会死循环,修改如下
-- 定义一个计算阶乘的函数
function fact(n)
if n <= 0 then
return 1
else
return n * fact(n - 1)
end
end
print("enter a number")
a = io.read("*n")
while a < 0 do
print("number is negative, enter a number")
a = io.read("*n")
end
print(fact(a))
1.2
-l
载入库,在lua解释器之外运行。
lua -l lib1
dofile
读取文件,在lua解释器之内运行。
print("use dofile")
dofile("1.1.lua")
1.4
end 为关键字,until?带?无关字符,nil为关键字,one-step中带-无关字符,这几个不是
1.5
false,因为type函数总是返回字符串。
1.6
--a = true
a = false
--a = 1
--a = "true"
print(a == true or a == false)
1.7
不是必须的,加括号更清晰。
1.8
print(arg[0])
2.0 ⭐
八皇后问题有92解。皇后可以在横、竖、斜线上不限步数地吃掉其他棋子。如何将8个皇后放在棋盘上(有8 * 8个方格),使它们谁也不能被吃掉!这就是著名的八皇后问题。
回溯法
N = 8 -- 棋盘大小
FuncCount = 0 --调用IsPlaceOK的次数
-- 检查第n行第c列皇后会不会被前面已有的n-1个皇后攻击
function IsPlaceOK(a, n, c)
for i = 1, n - 1 do
if a[i] == c or n - i == a[i] - c or n - i == c - a[i] then
return false
end
end
return true
end
-- 打印棋盘
function PrintSolution(a)
for i = 1, N do
for j = 1, N do
io.write(a[i] == j and "x" or "-", " ")
end
io.write("\n")
end
io.write("\n")
end
-- 放置从n到N的皇后,递归
function AddQueen(a, n)
if n > N then
PrintSolution(a)
else -- 放置第n个皇后
for c = 1, N do
ok = IsPlaceOK(a, n, c)
FuncCount = FuncCount + 1
if ok then
a[n] = c
AddQueen(a, n + 1)
end
end
end
end
AddQueen({}, 1)
print(string.format("调用IsPlaceOK的次数%d", FuncCount))
2.1
修改打印棋盘函数,调用操作系统函数,首次打印时提前退出
os.exit()
2.2 ⭐
全排列问题,胡凡算法笔记第115页。需要一个额外的表来记录状态。
N = 8 -- 棋盘大小
HashTable = {} -- 记录状态
Count = 0 -- 记录数量
FuncCount = 0 --调用IsPlaceOK的次数
for i = 1, N do
HashTable[i] = false
end
-- 打印棋盘
function PrintSolution(a)
for i = 1, N do
for j = 1, N do
io.write(a[i] == j and "x" or "-", " ")
end
io.write("\n")
end
io.write("\n")
end
-- 检查排列
function IsPlaceOK(a)
for i = 1, N do
for j = i + 1, N do
if a[i] == a[j] or j - i == a[i] - a[j] or j - i == a[j] - a[i] then
return
end
end
end
PrintSolution(a)
Count = Count + 1
end
-- 生成皇后
function AddQueen(a, n)
if n > N then
IsPlaceOK(a)
FuncCount = FuncCount + 1
end
for i = 1, N do
if HashTable[i] == false then
a[n] = i
HashTable[i] = true
AddQueen(a, n + 1)
HashTable[i] = false
end
end
end
AddQueen({}, 1)
print(string.format("共%d种方法", Count))
print(string.format("调用IsPlaceOK的次数%d", FuncCount))
2.0调用IsPlaceOK的次数15720,2.2调用IsPlaceOK的次数40320
3.1
print(0e12)
-- print(.e12)
-- print(0.0e)
print(0x12)
-- print(0xABFG)
print(0xA)
-- print(FFFF)
print(0xFFFFFFFF)
-- print(0x)
print(0x1P10)
print(0.1e1)
print(0x0.1p0)
print(0x0.Fp0)
print(0x0.1p1)
print(0x0.Fp1)
3.2
print(0xFFFFFFFFFFFFFFFF) -- -1
print(0x8000000000000000) -- 最小的负整数
print(0x7FFFFFFFFFFFFFFF)
print(0x7FFFFFFFFFFFFFFF * 2) -- 回环,对最高位取舍,等价于
print(0xFFFFFFFFFFFFFFFE)
print(0x8000000000000000 * 2) -- 回环,对最高位取舍,等价于
print(0x0000000000000000)
-- math.maxinteger除首位外全1,相乘后得到除最后一位和首部外,中间全0,超过符号位以外的部分被抛弃,其余全为0,最后只剩最后一位的1,所以结果为1。
-- math.mininteger除首位外全0,超出符号位以外的数值均被抛弃,其余全为0,所以结果为0。
--[[
二进制乘法要求满二进一原则
"_"符号为舍弃高位
求解:0x7FFFFFFFFFFFFFFF * 2
01111111 <= 0x7F
* 0010 <= 0x2
=============
00000000
01111111
=============
_11111110 => 0xFE
求解:0x8000000000000000 * 2
10000000 <= 0x80
* 0010 <= 0x2
=============
00000000
10000000
=============
_00000000 => 0x00
求解:math.maxinteger * math.maxinteger
0111
* 0111
=============
0111
0111
0111
0111
=============
_0001 => 1D
范例
1001
* 1101
=============
1001
0000
1001
+ 1001
=============
1110101
]]
3.3
-10 2
-9 0
-8 1
-7 2
-6 0
-5 1
-4 2
-3 0
-2 1
-1 2
0 0
1 1
2 2
3 0
4 1
5 2
6 0
7 1
8 2
9 0
10 1
3.4
print(2 ^ 3 ^ 4)
print(2 ^ (3 ^ 4))
print((2 ^ 3) ^ 4)
print(2 ^ -3 ^ 4) -- 幂运算优先级大于"-"
print(2 ^ (-3) ^ 4)
print(2 ^ -(3 ^ 4))
-- 2.4178516392293e+024
-- 2.4178516392293e+024
-- 4096.0
-- 4.1359030627651e-025
-- 2.4178516392293e+024
-- 4.1359030627651e-025
3.5
不懂
print(12.7 == 25.4/2)
print(12.7 == 406.4 / 2^5)
print(5.5 == 55/10)
print(5.5 == 11/2)
print(5.5 == 22/4)
print(5.5 == 176/2^5)
3.6 ⭐
function CalCloneVolume(r, h)
return math.pi * r ^ 2 * h / 3
end
print(CalCloneVolume(2, 3) == 12.566370614359) -- false 的原因
answer-1
You're multiplying something by pi, so the result is an irrational number which has a non-repeating fractional portion. .566370614359 is only the first 12 decimal places of a sequence that continues forever.
Internally, Lua uses a 64-bit floating point representation (on most platforms), so it only contains an approximation of that fraction, but it has a lot more precision than 12 decimal places. You'd have to understand the IEEE-754 floating point pretty intimately to predict the exact value for an equality comparison like that.
I can get true on my machine if I go out to at least 15 decimal places, but you shouldn't use floating point numbers that way.
I suspect you got 12.566370614359 by just printing the output of CalCloneVolume(2, 3). You're getting 12 decimal places there only because that's the default inside some routine in Lua that formats floating point numbers for display. You could show more decimal places like this:print(string.format("%.50g", CalCloneVolume(2, 3)))
answer-2
Generally it’s best not to compare floating point numbers for exactly equality unless you know they are storing integers. Integers (within a certain range) are stored precisely - so integer comparisons are ok.
Other real numbers (with fractional parts) are stored in binary not in decimal, which means that it’s hard to know what the exact number is - and some numbers can’t represented exactly. Irrational numbers are one example for both decimal and binary, but there are plenty of others - and they are not the same for binary and decimal.
Additionally different machines can have different bits, e g. 64 bits are common with PC, but you might want to only use 32 bits on a embedded device. Some machines use 80 bits. Additionally what they use these bit for inside a floating point number vary.
Additionally you can get problems with certain operations - divide for instance is the normal trouble maker. I notice you are dividing by 3. If you divide 1 by three you can’t represent this number exactly as with a finite number of digits on decimal or binary. (You can in ternary… base 3). In decimal it’s 0.333333333…forever and a 0.0101001010101010101… forever in binary but 0.1 in base 3. With floating point numbers you are only using a fixed number of bits.
As an example you might end up with three lots of 1/3 added together … which mathematically equals one, but in decimal representation looks like 0.99999999999 - something which confuses students today.
Comparing for higher then > or lower than < is fine. As of compare higher or equal >= or lower than equal <=.
If you need to compare for equality where you think you can get an approximate number … then you can check if it’s close to a number … for instance if x > 0.9 and x < 1.1
People get upset that some calculation ends up as 5.000000001 but floating point numbers are really cool as long as you understand their limitations.
3.7 ⭐
其他正态分布都可以用 0-1 正态分布 N(0,1) 生成。由概率论知, 若 \(Y_{1}, Y_{2}, \cdots, Y_{N}\) 是 n 个独立同分布的随机变量, 当 n 足够大时 \(X=Y_{1}+Y_{2}+\cdots+Y_{n}\) 就近似于一个正态分布.一般来说, 当 n>6 时, X 就是一个正态分布的很好的近似. X 的均值和方差分别为
\(\begin{array}{c} \mu_{x}=\mu_{1}+\mu_{2}+\cdots+\mu_{n}=n \mu \\ \sigma_{x}^{2}=\sigma_{1}^{2}+\sigma_{2}^{2}+\cdots+\sigma_{n}^{2}=n \sigma^{2} \end{array}\)
\(\begin{array}{c} X=Y_{1}+Y_{2}+\cdots+Y_{n} \\ z=\frac{\sum_{i=1}^{n} y_{i}-n \mu}{\sqrt{n \sigma^{2}}}, \quad z \in N(0,1) \end{array}\)
令 \(Y_{i}\) 为 0-1 均匀分布 U(0,1) , 已知 \(\mu=\frac{1}{2}, \sigma=\frac{1}{2 \sqrt{3}}\) , 代入
\(z=\frac{\sum_{i=1}^{n} y_{i}-n / 2}{\sqrt{n / 12}}\)
取 n=12 , 得 \(z=\sum_{i=1}^{12} y_{i}-6\)
给定数学期望和标准差, 计算 \(x=\mu+\sigma Z\) 便可得到服从 \(N\left(\mu, \sigma^{2}\right)\) 正态分布的随机数 x
-- 正态分布随机数发生器
function NormalDistribution(mean, std) -- 给出范围
local z = 0
for i = 1, 12, 1 do
z = z + math.random()
end
z = z - 6
return z * std + mean
end
local count = 0
local threshold = 2.576 -- 1.645
for i = 1, 10 ^ 5, 1 do
x = NormalDistribution(0, 1)
if x < threshold and x > -threshold then
count = count + 1
end
end
print(count)
print(string.format("%.6f", count / 10 ^ 5))
4.1
xml = [==[
<![CDATA]
Hello World
]]>]==]
print(xml)
xml2 = "\z
<![CDATA]\n\z
Hello World\n\z
]]>"
print(xml2)
xml3 = "\060\033\091CDATA\091\010Hello\032world\010\093\093\062"
print(xml3)
4.2
考虑可读性和字符串最大长度,可以使用 [==[ ... ]==]
或 \z
的形式。
如果字符串不是很多,可以用 \ddd
、\xhh
、\u{h..h}
来表示字符串。
4.3
function insert(a, index, b)
return string.sub(a, 1, index - 1) .. b .. string.sub(a, index, -1)
end
print(insert("hello world", 1, "start: "))
print(insert("hello world", 7, "small "))
4.4
function insert(a, index, b)
index = utf8.offset(a, index)
return a:sub(1, index - 1) .. b .. a:sub(index, -1)
end
print(insert("résumé", 7, "!"))
print(insert("résumé", 6, "!"))
4.5
function remove(s, i, k)
return s:sub(1, i - 1) .. s:sub(i + k, -1)
end
print(remove("hello world", 7, 4))
4.6
function remove(s, i, k)
i = utf8.offset(s, i)
k = utf8.offset(s, i + k)
return s:sub(1, i - 1) .. s:sub(k, -1)
end
print(remove("résumé", 2, 2))
4.7
function ispali(s)
for i = 1, #s // 2 do
if s:byte(i) ~= s:byte(-i) then
return false
end
end
return true
end
print(ispali("banana"))
print(ispali("step on no pets"))
5.1
sunday = "monday";
monday = "sunday"
t = {
sunday = "monday",
[sunday] = monday
}
print(t.sunday, t[sunday], t[t.sunday])
-- monday sunday sunday
5.2
a = {}
a.a = a
print(a)
print(a.a)
print(a.a.a)
print(a.a.a.a)
print(a == a.a)
print(a.a.a == a.a.a.a)
--[[
table: 00000000009e0f50
table: 00000000009e0f50
table: 00000000009e0f50
table: 00000000009e0f50
true
true
]]
a.a.a.a = 3 -- => {}.a = 3
print(a)
print(a.a)
-- print(a.a.a) -- attempt to index a number value
5.3
a = {
["\007"] = "\\a",
["\008"] = "\\b",
["\009"] = "\\t",
["\010"] = "\\n",
["\011"] = "\\v",
["\012"] = "\\f",
["\013"] = "\\r",
["\\"] = "\\\\",
["\""] = "\\\"",
["\'"] = "\\'"
}
for k, v in pairs(a) do
print(k, v)
end
5.4
function calfunc(t, x)
local sum = 0
for i, v in ipairs(t) do
sum = sum + v * x ^ (i - 1)
end
return sum
end
t = {1, 2, 1}
print(calfunc(t, 4))
5.5
function calfunc(t, x)
local sum = 0
local xb = x
if t[1] then
sum = t[1]
end
for i = 2, #t, 1 do
sum = sum + t[i] * x
x = x * xb
end
return sum
end
t = {1, 2, 1}
print(calfunc(t, 4))
5.6
function isValidSequence(t)
for i = 1, #t, 1 do
if t[i] == nil then
return false
end
end
return true
end
print(isValidSequence({1, 2, 3, 4, 5}))
print(isValidSequence({1, 2, 3, 4, 5, nil}))
print(isValidSequence({1, 2, 3, nil, 5}))
print(isValidSequence({nil, 2, 3, 4, 5}))
-- pairs\ipairs 会跳过nil键值对
5.7
function moveTable(a, b, i)
table.move(b, i, #b, i + #a)
table.move(a, 1, #a, i, b)
end
a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
b = {-1, -2, -3}
moveTable(a, b, 2)
for k, v in pairs(b) do
print(k, v)
end
5.8
..
多次拼接,每次创建新字符串,性能低
function concat(t)
local s = ""
for i, v in ipairs(t) do
s = s .. v
end
return s
end
print(concat({"hello", " ", "world"}))
print(table.concat({"hello", " ", "world"}))
a = {}
for i = 1, 2 ^ 19, 1 do
a[i] = 'a'
end
start = os.clock()
concat(a)
print(os.clock() - start)
start = os.clock()
table.concat(a)
print(os.clock() - start)
--[[
hello world
hello world
8.1589999999997
0.0070000000014261
]]
6.1
function showArray(list)
for i = 1, #list, 1 do
print(list[i])
end
end
showArray({1, 2, 3, 4, 5})
6.2
function showArray(...)
for i, v in ipairs({...}) do
if i ~= 1 then
print(v)
end
end
end
showArray(1, 2, 3, 4, 5)
6.3
function showArray(...)
t = {...}
for i, v in ipairs(t) do
if i ~= #t then
print(v)
end
end
end
showArray(1, 2, 3, 4, 5)
6.4
-- 排列问题
function randomArray(t, index, state)
index = index or {}
state = state or {}
if #index == #t then
for i = 1, #t, 1 do
print(t[index[i]])
end
return index
end
repeat
a = math.random(1, #t)
until state[a] ~= true
state[a] = true
index[#index + 1] = a
return randomArray(t, index, state) -- 尾调用消除
end
randomArray({1, 2, 3, 4, 5})
-- for i, v in ipairs(randomArray({1, 2, 3, 4})) do
-- print(i, v)
-- end
6.5
res = {}
tmp = {}
function showCombine(arr, x)
choose(arr, 1, 1, x)
end
-- arr-数组 n-选择组合中第几个 i-从第几个索引开始取 x-需要取多少个元素
function choose(arr, n, i, x)
if (n > x) then
res[#res + 1] = {table.unpack(tmp)}
return
end
for k = i, #arr do
tmp[n] = arr[k]
choose(arr, n + 1, k + 1, x)
end
end
showCombine({1, 2, 3, 4, 5}, 3)
for i = 1, #res do
for z = 1, #res[i] do
io.write(res[i][z])
end
io.write("\n")
end
7.1
function sortTXT(from, to)
local t = {}
from = from or ""
to = to or ""
if from ~= "" then
local f = assert(io.open(from, "r"))
for line in f:lines() do
t[#t + 1] = line
end
f:close()
else
io.input(io.stdin)
for line in io.lines() do
t[#t + 1] = line
if line == "" then
break
end
end
end
table.sort(t)
if to ~= "" then
f = assert(io.open(to, 'w'))
for _, v in ipairs(t) do
f:write(v, "\n")
end
f:close()
else
for _, v in ipairs(t) do
io.write(v, "\n")
end
end
end
-- sortTXT()
sortTXT("./test.txt")
sortTXT("./test.txt", "./test2.txt")
7.2
function sortTXT(from, to)
local t = {}
from = from or ""
to = to or ""
if from ~= "" then
local f = assert(io.open(from, "r"))
for line in f:lines() do
t[#t + 1] = line
end
f:close()
else
io.input(io.stdin)
for line in io.lines() do
t[#t + 1] = line
if line == "" then
break
end
end
end
table.sort(t)
if to ~= "" then
err = io.open(to, 'r')
if err ~= nil then
err:close()
io.stdout:write("当前文件已存在,确定请输 1\n")
ok = io.stdin:read("l")
if ok ~= "1" then
return
end
end
f = assert(io.open(to, 'w'))
for _, v in ipairs(t) do
f:write(v, "\n")
end
f:close()
else
for _, v in ipairs(t) do
io.write(v, "\n")
end
end
end
sortTXT("./test.txt", "./test2.txt")
7.3
io.input("./huge.txt")
start = os.clock()
for count = 1, math.huge do
s = io.read(1)
if s == nil then
break
end
io.write(s)
end
io.write(os.clock() - start)
-- 3.839 26.559
-- start = os.clock()
-- for count = 1, math.huge do
-- s = io.read('l')
-- if s == nil then
-- break
-- end
-- io.write(s)
-- end
-- io.write(os.clock() - start)
-- 0.654 4.309
-- start = os.clock()
-- for count = 1, math.huge do
-- s = io.read(2 ^ 13)
-- if s == nil then
-- break
-- end
-- io.write(s)
-- end
-- io.write(os.clock() - start)
-- 0.579 4.122
-- start = os.clock()
-- s = io.read('a')
-- io.write(s)
-- io.write(os.clock() - start)
-- 0.5969999 4.201
7.4
function lastLine(file)
local f = io.open(file, 'r')
local current = f:seek()
local size = f:seek("end")
local tmp = ""
f:seek("set", current)
if size > 2 ^ 20 then
f:seek("set", size // 2 + size // 4 + size // 8) -- 切到 7/8 进度
end
for line in f:lines() do
tmp = line
end
io.write(tmp, "\n")
f:close()
end
lastLine("./huge.txt")
7.5
function lastLine(file, n)
local f = io.open(file, 'r')
local current = f:seek()
local size = f:seek("end")
local tmp = {}
f:seek("set", current)
if size > 2 ^ 20 then
f:seek("set", size // 2 + size // 4 + size // 8) -- 切到 7/8 进度
end
for line in f:lines() do
tmp[#tmp + 1] = line
end
for i = 1, n, 1 do
io.write(tmp[#tmp - n + i], "\n")
end
f:close()
end
lastLine("./huge.txt", 3)
8.2
while true do
end
for i = 1, math.huge do
end
repeat
until nil
::s1::
do
goto s1
end
8.4
function room1()
local move = io.read()
if move == "south" then
return room3()
elseif move == "east" then
return room2()
else
print("invalid move")
return room1()
end
end
function room2()
local move = io.read()
if move == "south" then
return room4()
elseif move == "west" then
return room1()
else
print("invalid move")
return room2()
end
end
function room3()
local move = io.read()
if move == "north" then
return room4()
elseif move == "east" then
return room1()
else
print("invalid move")
return room3()
end
end
function room4()
print("Congratulations, you won!")
end
room1()
8.5
不同函数之间如果能使用goto跳转会导致代码可读性变差,不利于维护,其次,直接跳转到某个变量的作用域,却绕过了变量的声明将会发生无法预料的错误
goto
print(1919180)
-- goto bb
do
local x = 1
goto bb
::cc:: -- 不能跳转到局部变量作用域
x = 3
print(x)
::dd:: -- 可以跳转到这,局部变量作用域终止于声明变量的代码块中的最后一个有效语句
end
::bb::
do
::aa:: -- 可见性原则
print("114514")
end
-- goto aa
-- 另外不能跳转到函数外