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参数和dofile运行twice示例,并感受你喜欢哪种方式

-l

载入库,在lua解释器之外运行。

lua -l lib1

dofile

读取文件,在lua解释器之内运行。

print("use dofile")
dofile("1.1.lua")

1.4

以下字符串中哪些是有效的标识符

___ _end End end until? nil NULL one-step

end 为关键字,until?带?无关字符,nil为关键字,one-step中带-无关字符,这几个不是

1.5

表达式type(nil)==nil的值是什么? 能解释原因吗

false,因为type函数总是返回字符串。

1.6

除了使用函数type外,如何检查一个值是否为Boolean类型?

--a = true
a = false
--a = 1
--a = "true"
print(a == true or a == false)

1.7

考虑如下的表达式

(x and y and (not z)) or ((not y) and x) 其中的括号是否是必须的?

不是必须的,加括号更清晰。

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 ⭐

解决八皇后问题的另一种方式是,先生成1~8之间的所有排列,然后一次遍历这些排列,检查每一个排列是否是八皇后问题的有效解。请使用这种方法修改程序,并对比新程序与旧程序之间的性能差异,比较isplaceok函数的次数

全排列问题,胡凡算法笔记第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

以下哪些是有效的数值常量?它们的值分别是多少

.0e12 .e12 0.0e 0x12 0xABFG 0xA FFFF 0xFFFFFFFF 0x 0x1P10 0.1e1 0x0.1p1

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

解释下列表达式之所以得出响应结果的原因(整型算术运算总是会回环

math.maxinteger * 2 --> -2

math.mininteger * 2 --> 0

math.maxinteger * math.maxinteger --> 1

math.mininteger * math.mininteger --> 0

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

下列代码的输出结果是什么

for i = -10 , 10 do 
    print(i,i%3)
end
-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

表达式 2 ^ 3 ^ 4的值是什么?表达式 2 ^ -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 ⭐

利用函数math.random编写一个生成遵循正态分布的伪随机数发生器

其他正态分布都可以用 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

请问如何在Lua程序中以字符串的方式使用如下的XML片段,请给出至少两种实现方式

<![CDATA]
Hello World
]]>
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

使用UTF-8字符串重写4.3,起始位置和长度都是针对代码点而言的

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

使用UTF-8字符串重写4.5,起始位置和长度都是针对代码点而言的

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的值是什么,其中每个a都一样吗

如果将如下代码 a.a.a.a=3 追加到上述代码中,现在 a.a.a.a 的值变成了什么

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

编写一个函数,该函数以多项式(使用表表示)和值x为参数,返回结果为对应多项式的值

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

改写5.4,使之最多使用n个加法和n个乘法,且没有指数

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

实现表标准库函数 table.concat 并进行性能对比

.. 多次拼接,每次创建新字符串,性能低

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(...)
    local t = {}
    for i, v in ipairs({...}) do
        if i ~= 1 then
            t[#t + 1] = v
        end
    end
    return t
end

t = showArray(1, 2, 3, 4, 5)
print(table.unpack(t))

6.3

请编写一个函数,该函数的参数为可变数量的一组值,返回值为除最后一个元素之外的其他所有值

function showArray(...)
    local t = {}
    for i, v in ipairs({...}) do
        if i ~= #{...} then
            t[#t + 1] = v
        end
    end
    return t
end

t = showArray(1, 2, 3, 4, 5)
print(table.unpack(t))

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

ANTI SPIDER BOT -- www.cnblogs.com/linxiaoxu

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

请改写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
        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

对比使用下列几种不同的方式把标准输入流复制到标准输出流中的Lua程序的性能表现

  • 按字节
  • 按行
  • 按块 8KB
  • 一次性读取
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

请编写一个程序,该程序输出一个文本文件的最后一行。当文件较大且可以使用seek时,请尝试避免读取整个文件。

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

修改7.4,使其可以输出一个文本文件的最后n行。同时,当文件较大且可以使用seek时,请尝试避免读取整个文件。

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

描述Lua语言中实现无条件循环的4种不同方法

while true do
end

for i = 1, math.huge do
end

repeat
until nil

::s1::
do
    goto s1
end

8.4

将迷宫游戏的goto语句全部修改为尾调用消除形式

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

解释一下为什么Lua语言会限制goto语句不能跳出一个函数

不同函数之间如果能使用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

-- 另外不能跳转到函数外