更简单的 For 迭代器

lua-users home
wiki

当前迭代器 "for" 循环的模型可能可以简化。以下是一个尝试解释当前模型并介绍一个更简单的替代方案的尝试——也许。似乎复杂性源于

当前语法如下

for A, data in iterator_func, X, Y do block end

Data 是函数返回的实际数据,并在后面的代码块中使用。A、X 和 Y 的解释将在下面进一步说明。以下是一个基于教程示例的集合迭代器和生成器迭代器的可能实现(尝试非常明确,并从 1 开始计数)

-- collection iterator --
numbers = {1,3,5,7,9,11,13}
function coll_squares(coll)
    local function next_square(coll, index)
        if index > #coll then
            return nil
        end
        n = coll[index]
        return index+1, n*n
    end
    return next_square, coll, 1
end
for i, square in coll_squares(numbers) do print (square) end     --> OK

-- generator iterator --
function gen_squares(limit)
    local function next_square(limit, number)
        if number > limit then
            return nil
        end
        return number+1, number*number
    end
    return next_square, limit, 1
end
for n, square in gen_squares(7) do print (square) end     --> OK

那么,A、X 和 Y 是什么呢?在集合的情况下

在生成器的情况下

很难找到一个共同点来有意义地解释和命名 A、X 和 Y。X 在参考手册中被称为“s”,在教程中被称为“state”。在参考手册中,A 被称为 var1,而 Y 被称为 var。以下是一个尝试理解这些名称的尝试

(如果有人找到更好的名称……)除了用于生成下一个数据之外,mark 和 range 也一起用于确定何时停止迭代。很难猜测迭代器和迭代器函数应该返回什么,以及函数从 lua 隐式接收什么,以及所有这些值的正确顺序。

上面的代码可以改写如下

-- collection iterator --
function coll_squares(coll)
    local index = 1
    local coll = coll       -- just to make things clear
    local function next_square()
        if index > #coll then
            return nil
        end
        n = coll[index]
        index = index+1
        return n*n
    end
    return next_square
end
for square in coll_squares(numbers) do print (square) end     -- OK

-- generator iterator --
function gen_squares(limit)
    local number = 1
    local limit = limit     -- ditto
    local function next_square()
        if number > limit then
            return nil
        end
        n = number
        number = number+1
        return n*n
    end
    return next_square
end
for square in gen_squares(7) do print (square) end     -- OK

除了最后一个之外,其他都是简化,只有细微的差别

最后一点使标记(索引或数字)成为迭代器中的局部变量,该变量作为上值可供嵌套函数 _闭包_ 访问(对吗?)。“范围”只能是迭代器中的局部变量,因此无需将其显式地作为参数传递给函数。(如果此处有任何错误,包括术语,请更正)

我们可以想象更复杂的情况,例如指定生成器间隔。附加数据成为迭代器参数

-- generator iterator --
function gen_squares(start, stop, step)
    local number = start
    local function next_square()
        if number > stop then
            return nil
        end
        n = number
        number = number+step
        return n*n
    end
    return next_square
end
for square in gen_squares(3,9,2) do print (square) end     --> OK

同样,如果我们使集合迭代器复杂化(这里有点人为)

-- collection iterator --
require "math"
numbers = {1,3,5,7,9,11,13,15,17}
function coll_squares(coll, modulo)
    local index = 1
    local function number_filter()
        -- return next number in coll multiple of modulo, else nil
        while (index < #coll) do
            number = coll[index]
            if math.fmod(number, modulo) == 0 then
                return number
            end
            index = index+1
        end
        return nil
    end
    local function next_square()
        -- yield squares of multiples of modulo in coll
        n = number_filter()
        if not n then
            return nil
        end
        index = index+1
        return n*n
    end
    return next_square
end
for square in coll_squares(numbers, 3) do print (square) end     --> OK

在所有情况下,A、X 和 Y 似乎都不需要。这种实现迭代器的方式很好地利用了 Lua 的基本特性:函数作为值、嵌套函数、闭包/上值。因此,一个问题是:我们能否通过去除 A、X 和 Y 来简化“for”语法、迭代器和迭代器函数之间的接口?如果是,新的语法可能是

for data in iterator_func do block end
而当前的语法是
for A, data in iterator_func, X, Y do block end

因此,各种迭代器不会以复杂的方式被语法本身全局捕获,而是由用户实现来处理。这将使学习和解释语法以及为给定任务编写迭代器的正确方法变得更加容易。

参考手册指出

<< f、s 和 var 是不可见的变量。这些名称仅用于解释目的。 >>
在本提案中,它们不存在。必要的数据作为参数传递给迭代器,就像现在一样:集合、边界或其他任何东西。

(第一页内容由 DeniSpir 撰写)


最近更改 · 偏好设置
编辑 · 历史记录
最后编辑时间:2009 年 11 月 13 日下午 3:41 GMT (差异)