一个无声失败的Lua迭代器?
我有一个关于简单迭代器的非常简单的问题 假设我设计了一个函数,一个无声失败的Lua迭代器?,lua,iterator,assert,Lua,Iterator,Assert,我有一个关于简单迭代器的非常简单的问题 假设我设计了一个函数,files(),它迭代一个文件夹中的所有文件: for file in files("/path/to/folder") do print(file) end 现在,这看起来很完美,但这里有一个问题:如果文件夹不存在,或者我们没有对它的读取权限,该怎么办 我们如何指出这种错误 一种解决方案是让files()返回nil,在这种情况下,“无读取权限”。然后,我们就可以将对files()的调用封装在assert()中: 这似乎解决了问
files()
,它迭代一个文件夹中的所有文件:
for file in files("/path/to/folder") do
print(file)
end
现在,这看起来很完美,但这里有一个问题:如果文件夹不存在,或者我们没有对它的读取权限,该怎么办
我们如何指出这种错误
一种解决方案是让files()
返回nil,在这种情况下,“无读取权限”
。然后,我们就可以将对files()
的调用封装在assert()中:
这似乎解决了问题。但这迫使我们的用户总是使用assert()
。如果用户不关心错误怎么办?对于这类用户,我们希望我们的文件()
的行为就像文件夹是空的一样。但是Lua——如果files()
指示错误——将尝试调用返回的nil
,这将导致错误(“尝试调用nil值”)
所以
我们如何设计一个迭代器,files()
,既能满足关心错误的用户,又能满足不关心错误的用户
如果不可能,你会建议什么样的选择?
也有疑问,如果你在迭代过程中出错(例如,用递归递归来访问子文件夹),你会做什么。在这种情况下,断言没有帮助
在本例中,我创建了两种不同的迭代器(内部和外部)
或仅创建2个不同的迭代器。
第一:不要返回<代码> NIL< /Cord>+错误消息,考虑在<代码>文件< /代码>函数中使用一个错误(使用<代码>错误< /代码>)。这样您就不会忘记
assert
调用,也不会出现令人困惑的“尝试调用nil值”错误
当您不想引发错误时,可以向文件
传递一个额外的布尔参数——在这种情况下,您应该返回一个空函数(function()end
),而不是调用error
更一般的方法如下:
-- an iterator that immediately stops a for loop
local function dummy_iter() end
-- catch errors and skip for loop in that case
function iterpcall( g, ... )
local ok, f, st, var = pcall( g, ... )
if ok then
return f, st, var
else
return dummy_iter
end
end
for file in iterpcall( files, "/path/to/folder" ) do
print( file )
for line in iterpcall( io.lines, file ) do -- works for other iterators as well
print( line )
end
end
上面的iterpcall
的实现只处理迭代器生成器(文件
或io.lines
)中出现的错误,而不是迭代器函数(f
)本身。要做到这一点,您必须将f
用pcall
封装在一个闭包中。谢谢你们的回答。事实上,一旦我们抛弃了一个函数应该同时以两种不同方式运行的想法,一切就变得容易了。
-- raise error
for file in files(...) do ... end
-- return error
files(...,function(file) ... end)
-- an iterator that immediately stops a for loop
local function dummy_iter() end
-- catch errors and skip for loop in that case
function iterpcall( g, ... )
local ok, f, st, var = pcall( g, ... )
if ok then
return f, st, var
else
return dummy_iter
end
end
for file in iterpcall( files, "/path/to/folder" ) do
print( file )
for line in iterpcall( io.lines, file ) do -- works for other iterators as well
print( line )
end
end