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

Nic*_* M. 4 lua assert iterator

我有一个简单迭代器的非常简单的问题.

假设我设计了一个files()迭代文件夹中所有文件的函数:

for file in files("/path/to/folder") do
  print(file)
end
Run Code Online (Sandbox Code Playgroud)

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

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

一种解决方案是在这种情况下files()返回nil, "no read permission".然后我们可以将调用包装到files()内部assert():

for file in assert(files("/path/to/folder")) do
  print(file)
end
Run Code Online (Sandbox Code Playgroud)

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

所以,

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

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

sif*_*joe 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
Run Code Online (Sandbox Code Playgroud)

iterpcall上面的实现只处理迭代器生成器(filesio.lines)中引发的错误,而不是迭代器函数(f)本身.你必须f用一个封装来封装pcall它.