笔试
-
函数冒号与点的区别?
冒号的第一个参数默认为self,指向调用该函数的表。
生存周期相同,作用域不同,普通全局变量可以作用于所有文件,extern声明即可,而static全局变量只能作用于当前文件。
-
解释下lua中的元表和元方法?
元表metetable : 允许该表table行为,行为关联元方法,类似一种“操作指南”,包含各种操作行为的解决方案!
元方法:当表执行某些操作失败的时候,操作指南里的元方法指导你的行为。
-
index和newindex元方法的区别?
访问不存在的数据,由__index提供最终结果
__index元方法可以是一个函数,Lua语言就会以【表】和【不存在键】为参数调用该函数
__index元方法也可以是一个表,Lua语言就访问这个元表
-
require,loadfile和dofile的区别?
在lua中我们引用其他模块的时候,可以使用三种方法:require,loadfile和dofile。
首先区分loadfile,loadfile只负责编译并不会执行模块代码,而require和dofile都会编译且执行。
| 知识点 | 核心答案 |
|---|---|
| 闭包与Upvalue | 内层函数持有外层局部变量(upvalue),实现状态保持 |
**__index** | 查询不存在的键时触发,用于实现继承链 |
**__newindex** | 赋值不存在的键时触发,用于拦截赋值操作 |
| 深拷贝 | 递归复制表的所有层级(需手动实现) |
请写一个多值返回的函数
function foo2 () return 'a','b' end
请写一个可变参数的函数
function g (a, b, ...) end
g(3, 4, 5, 8) a=3, b=4, arg={5, 8; n=2}
Lua如何实现C#构造函数new方法
new传进的参数当做一个本地表, 元表本身Self作为元表,设置元方法,将其作为参数表o的元表,并返回出去 从而实现C# new构造函数。
调用new的时候,其实访问的是元表内容
function Class:(o) -- 传入self为Account
o = o or {}
self.__index = self --直接把表Class当做元表
setmetatable(o, self)
return o
end
local object = Class:new(o) --object变量可以访问元表
请写一个带有不定参数的lua函数,并输出所有的参数
function test( ... )
local args = { ... }
for k,v in pairs(args) do
print(k,v)
end
end
如下一段程序,请在TODO处插入代码,使后面对table新建值时提示错误,并使其无效
local table = setmetatable({}, {})
table.key = "iam key"
table.value = 123
print(table.key)
---- TODO:在这里插入你的代码
答案:
local mt = getmetatable(table) -- 获得table的元表
function mt:__newindex(key,value) -- 添加__newindex元方法
table[key] = nil
print("cannot create new property" .. key)
end
如何实现一个 Lua table的迭代器
-- 假设我们有一个table
local myTable = {
a = 1,
b = 2,
c = 3
}
-- 自定义迭代器函数
-- 这个迭代器将按照插入顺序遍历table(注意:Lua table不保证顺序,这里仅为示例)
-- 但为了演示,我们假设或强制table有一个明确的顺序
local function iterator(table, keys)
local index = 0
return function()
index = index + 1
if index > #keys then
return nil -- 迭代结束
end
local key = keys[index]
return key, table[key] -- 返回键值对
end
end
-- 假设我们知道或能获取到table的键的顺序
local keys = {"a", "b", "c"} -- 在实际应用中,你可能需要动态获取这个顺序
-- 创建迭代器
local iter = iterator(myTable, keys)
-- 使用迭代器遍历table
for key, value in iter() do
print(key, value)
end
注意
上面的例子假设或强制了table的键有一个明确的顺序(通过keys数组),但在Lua中,普通的table并不保证键的插入顺序(Lua 5.2及以后版本引入了table.pack和table.unpack,但主要用于固定大小的数组,并不直接解决普通table的迭代顺序问题)。对于普通table,如果你需要按照特定的顺序迭代,你可能需要维护一个额外的数组来存储键的顺序。
使用内置的 pairs 函数 快捷返回迭代器
for key, value in pairs(myTable) do
print(key, value)
end
pairs函数返回一个迭代器,它会遍历table中的所有键值对,但遍历的顺序取决于table的内部实现,可能不是插入顺序。
如果你需要按照插入顺序遍历,且你的Lua版本是5.3或更高,可以考虑使用table.move函数(虽然它本身不直接用于迭代,但可以用于在迭代前重新排序table的键)。
然而,对于大多数用途,简单地使用pairs可能就足够了。
实现替换字符串"abcdefgh"中的"abc"为"ddc"
string.gsub("abcdefgh","abc","ddc")
用Lua实现:提供一个功能,实现table只读
在Lua中,直接实现一个完全不可变的(只读)table是不可能的,因为Lua的table是动态数据结构,其元素可以在运行时被修改。但是,我们可以通过封装和元表(metatable)来模拟一个只读table的行为。
以下是一个简单的示例,展示了如何创建一个函数来返回一个“只读”的table。这个“只读”table实际上是通过一个代理table来实现的,该代理table会拦截所有尝试修改原始table的操作,并抛出错误或简单地忽略这些操作。
function createReadOnlyTable(originalTable)
local readOnlyTable = {}
local metatable = {
__index = function(t, key)
return originalTable[key]
end,
__newindex = function(t, key, value)
error("Attempt to modify a read-only table", 2)
end
}
setmetatable(readOnlyTable, metatable)
return readOnlyTable
end
-- 示例使用
local original = {
a = 1,
b = 2,
c = 3
}
local readOnly = createReadOnlyTable(original)
print(readOnly.a) -- 输出: 1
-- 尝试修改只读table
readOnly.a = 10 -- 这将触发错误
-- 捕获并处理错误
try = function(func)
local ok, result = pcall(func)
if not ok then
print("Error:", result)
end
end
try(function() readOnly.a = 10 end) -- 输出: Error: Attempt to modify a read-only table
-- 原始table仍然可以修改
original.a = 10
print(readOnly.a) -- 输出: 10,因为只读table是通过原始table的索引来访问的
在这个示例中,createReadOnlyTable函数接受一个table作为参数,并返回一个新的table,这个新table通过元表(metatable)的__index和__newindex方法被设置为只读。__index方法用于拦截对只读table的读取操作,并返回原始table中相应的值。__newindex方法用于拦截对只读table的写入操作,并抛出一个错误。
需要注意的是,虽然这个只读table的修改操作会被阻止,但原始table仍然可以被修改,这可能会影响通过只读table访问的数据。如果你需要完全隔离原始数据,你可能需要复制原始table的内容到一个新table中,并在这个新table上设置只读行为。
用Lua实现:生成10000000以内不重复的5555555个随机数
在Lua中生成大量不重复的随机数,特别是当数量接近或超过给定范围(如10000000)的一半时,需要一种有效的方法来确保随机数的唯一性。由于Lua标准库中的math.random()函数直接用于生成随机数可能会产生重复值,我们需要结合其他数据结构来确保唯一性。
一个常用的方法是使用集合(在Lua中通常使用表来模拟)来存储已经生成的随机数,并在每次生成新随机数时检查它是否已存在于集合中。然而,对于如此大量的随机数,这种方法可能会变得非常低效,因为随着集合的增长,检查新随机数是否已存在的成本也会增加。
一个更高效的方法是使用一种称为“洗牌算法”(Fisher-Yates Shuffle)的技术来随机化一个序列。对于这个问题,我们可以先创建一个包含1到10000000的整数序列,然后对这个序列进行随机洗牌,最后取前5555555个元素作为结果。
以下是一个Lua脚本的示例,展示了如何实现这一思路:
function shuffle(array)
local n = #array
for i = n - 1, 1, -1 do
local j = math.random(i)
-- 交换 array[i] 和 array[j]
local temp = array[i]
array[i] = array[j]
array[j] = temp
end
end
function generateUniqueRandomNumbers(count, max)
if count > max then
error("Count cannot be greater than max")
end
-- 创建一个包含1到max的整数序列
local numbers = {}
for i = 1, max do
numbers[i] = i
end
-- 对序列进行随机洗牌
shuffle(numbers)
-- 返回前count个元素
return table.slice(numbers, 1, count)
end
-- Lua标准库中没有table.slice函数,这里提供一个简单的实现
function table.slice(tbl, first, last)
local sliced = {}
for i = first, last do
sliced[i - first + 1] = tbl[i]
end
return sliced
end
-- 生成5555555个不重复的随机数
local uniqueRandomNumbers = generateUniqueRandomNumbers(5555555, 10000000)
-- 打印前10个随机数作为示例
for i = 1, 10 do
print(uniqueRandomNumbers[i])
end