一个Lua迭代器无声地失败?

时间:2014-06-11 07:41:32

标签: lua iterator assert

对于一个简单的迭代器,我有一个非常简单的问题。

让我们说我设计了一个函数files(),它迭代文件夹中的所有文件:

for file in files("/path/to/folder") do
  print(file)
end

现在,这看起来很完美,但这里有一个问题:如果该文件夹不存在,或者我们没有读取权限该怎么办?

我们如何表明这样的错误?

在这种情况下,一个解决方案是让files()返回nil, "no read permission"。然后我们就可以将来电files()打包到assert()内:

for file in assert(files("/path/to/folder")) do
  print(file)
end

这似乎解决了这个问题。但这迫使我们的用户始终使用assert()。如果用户不关心错误怎么办?对于此类用户,我们希望files()的行为就像文件夹为空。但是Lua --in case files()表示错误 - 会尝试调用返回的nil,这将导致错误("尝试调用nil值")。 / p>

所以,

我们如何设计一个迭代器files(),以满足关心错误的用户和不喜欢的用户?

如果不可能,你会建议什么选择?

2 个答案:

答案 0 :(得分:7)

首先:考虑在nil函数中引发错误(使用files),而不是返回error +错误消息。通过这种方式,您可以忘记assert来电,并且您不会感到困惑"尝试调用零值"错误。

如果您不想引发错误,可以将额外的布尔参数传递给files - 您应该返回一个空函数(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的实现只处理迭代器生成器filesio.lines)中引发的错误,而不是处理迭代器函数({{1} }})本身。你必须将f包装在一个带有f的闭包中。

答案 1 :(得分:3)

如果在迭代过程中出现错误(例如,使用重复迭代对子文件夹进行访问拒绝),还有一个问题是你要做什么。在这种情况下,断言没有帮助。

在这种情况下,我创建了2个迭代器变体(内部和外部)。

-- raise error
for file in files(...) do ... end

-- return error
files(...,function(file) ... end)

或者只创建2个不同的迭代器。