一个无声失败的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