范围迭代器

lua-users home
wiki

下面的 range 函数返回一个迭代器,可以在 for 循环中使用,而不是基本的 for i=i,j,k do end 语法。例如,当基本 for 循环的两个或三个参数由另一个函数返回时,可以使用它。-- JeromeVuarand

function range(from, to, step)
  step = step or 1
  return function(_, lastvalue)
    local nextvalue = lastvalue + step
    if step > 0 and nextvalue <= to or step < 0 and nextvalue >= to or
       step == 0
    then
      return nextvalue
    end
  end, nil, from - step
end

示例使用

function f() return 10, 0, -1 end

for i in range(f()) do
  print(i)
end

更新 #1

条件也可以从函数中移出:--DavidManura

function range(from, to, step)
  step = step or 1
  local f =
    step > 0 and
      function(_, lastvalue)
        local nextvalue = lastvalue + step
        if nextvalue <= to then return nextvalue end
      end or
    step < 0 and
      function(_, lastvalue)
        local nextvalue = lastvalue + step
        if nextvalue >= to then return nextvalue end
      end or
      function(_, lastvalue) return lastvalue end
  return f, nil, from - step
end

更新 #2

这是一个版本,除了将步长视为可选并优化内部条件检查外,还允许 1 个参数调用

-- range(a) returns an iterator from 1 to a (step = 1)
-- range(a, b) returns an iterator from a to b (step = 1)
-- range(a, b, step) returns an iterator from a to b, counting by step.
function range(a, b, step)
  if not b then
    b = a
    a = 1
  end
  step = step or 1
  local f =
    step > 0 and
      function(_, lastvalue)
        local nextvalue = lastvalue + step
        if nextvalue <= b then return nextvalue end
      end or
    step < 0 and
      function(_, lastvalue)
        local nextvalue = lastvalue + step
        if nextvalue >= b then return nextvalue end
      end or
      function(_, lastvalue) return lastvalue end
  return f, nil, a - step
end

这允许更紧凑的形式用于简单的向上计数
-- Prints the range of numbers 1 to 10 inclusive.
for i in range(10) do
  print(i)
end

更新 #3

这是我的看法,它支持正向和反向迭代。
另一个重要的事情是,迭代器(imo)_应该_在每次成功迭代时至少返回 2 个值。

考虑一下

       -- Some functions accept the returned values of generators (like ipairs())
       -- and have a predefined for loop in their body like so:
       some_function = (...) local tmp = {} for k, v in ... do tmp[k] = v end return tmp end
       some_function(ipairs({ 1, 2, 3 }) -> { 1, 2, 3 }
       some_function(range(3)) -> { 1, 2, 3 }

实际的 range() 定义

       -- range(start)             returns an iterator from 1 to a (step = 1)
       -- range(start, stop)       returns an iterator from a to b (step = 1)
       -- range(start, stop, step) returns an iterator from a to b, counting by step.
       range =
           function (i, to, inc)
                if i == nil then return end -- range(--[[ no args ]]) -> return "nothing" to fail the loop in the caller

               if not to then
                   to = i 
                   i  = to == 0 and 0 or (to > 0 and 1 or -1) 
               end 

               -- we don't have to do the to == 0 check
               -- 0 -> 0 with any inc would never iterate
               inc = inc or (i < to and 1 or -1) 

               -- step back (once) before we start
               i = i - inc 

               return function () if i == to then return nil end i = i + inc return i, i end 
           end 
       

为什么

我需要 range() 因为我想做

        to_table(range(10)) -- would create { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }

当然,你可以键入一个文字 { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } 来获取你想要的表,但这并不是重点,重点是拥有像 range() 这样的生成器可以让你做什么。它适用于你需要提供迭代器的地方,因为你不能使用数字 for 循环。(函数式 vs 命令式编程?)

在其他地方,我用它来做这样的事情

        -- print the sum of every cleanly-divisible-by-3 number from 1 to 100
        print(setmetatable(to_table(range(100)), { __index == 'special_table' }):remove_if(function (x) return x % 3 ~= 0 end):reduce(function (x, y) return x + y --[[ sum ]] end, 0 --[[ initial y ]]))

当然,还有更直接的方法来编写它——但让你的想象力发挥作用,以获得更好的例子。:-)

示例调用

        for i in range( 10) print(i) end -- iterate 1 to 10, increment by 1
        for i in range(-10) print(i) end -- iterate -1 to -10, decrement by 1
        for i in range(7, -2) print(i) end -- iterate 7 to -2, decrement by 1
        for i in range(3, 27, 3) print(i) end -- iterate 3 to 27, increment by 3
        for i in range(0, 1, -1) print(i) end -- iterate 0 to 1, decrementing by 1 (loop forever downward)
        for i in range() print(i) end -- error() because the call to the "returned" iterator is a nil value

-- SleepyCoder


最近更改 · 偏好设置
编辑 · 历史记录
最后编辑于 2012 年 8 月 5 日上午 9:55 GMT (差异)