为什么在Lua中要使用do-end块?

24

我一直试图找到答案,但都失败了。我想知道的是,do-end块实际上有什么用途?我的书上只说在需要时使用值,我该如何使用它?

我将一个函数放在do-end循环中,并将本地变量放在函数外部但在do-end块内以减少本地变量的作用域,然后变量将被函数看到吗?但是,函数还能被调用吗?

非常抱歉表述不清楚,希望这样讲解有意义。也许举个例子会更好理解 ^^

3个回答

31

do-end块与变量作用域问题有关。基本上,当您使用标识符时,它具有什么值?例如,当我们编写以下程序时将打印哪些数字?

local x = 10
if x > 0 then
    local x = 17
    print(x)
end
print(x)

当涉及到局部变量时,Lua使用标准的词法作用域,这在《Lua编程》书的第4.2节中有很好的解释。词法作用域之所以非常有用,是因为有以下几个原因:

  • 变量作用域是静态的。你仅通过查看源代码就可以知道你的代码中每个标识符对应的变量和函数。这与Bash中的动态作用域或通过方法调用或数组查找进行的间接分派相反,后者需要思考程序的执行流程才能知道最终的值。

  • 变量作用域是有限的,有助于阅读和避免一些错误:

    • 如果只在需要使用时声明变量,则可以同时声明和初始化变量。另一方面,如果在函数顶部声明所有变量,那么你可能会在初始化之前意外地使用一个变量。

    • 如果在内部作用域中定义变量,则不能在外部作用域中意外使用它。

  • 词法作用域能够与嵌套函数(闭包)结合使用,实现一些非常表达性的惯用语

通常情况下,你不需要担心自己指定变量作用域。函数、循环和条件语句会自动引入新的作用域,这通常足以为你的变量提供良好约束的作用域。尽管如此,在某些情况下,你可能希望凭空引入一些额外的作用域,这时我们可以使用 do-end。在《Lua编程》中有以下例子,你需要计算二次方程的解,并且计算过程中有一些临时变量:

do
  local a2 = 2*a
  local d = sqrt(b^2 - 4*a*c)
  x1 = (-b + d)/a2
  x2 = (-b - d)/a2
end          -- scope of `a2' and `d' ends here
print(x1, x2)

如果没有do-end块,a2d可能会在它们不再需要时被意外使用:

local a2 = 2*a
local d = sqrt(b^2 - 4*a*c)
x1 = (-b + d)/a2
x2 = (-b - d)/a2
print(x1, x2)

print(a2) -- OOPS! I wanted to say "print(a)"

话虽如此,使用 do-end 并不需要经常使用。如果代码块很小,则隐藏局部变量的必要性较小,如果代码块很大,则将代码块放入其自己的子例程中通常是更好的方法。我发现 do-end 发挥作用的时候是当你只需要进行一次计算时——函数可能被调用多次,但如果你使用 do-end 块,你就清楚地表明你只运行了那段代码一次。


15

是的,do end块可以用来限制变量的作用域;为了保持使用这些变量的函数可见,您有几个选项。

  1. Localize the variable that keeps the function outside of the block:

    local func    
    do
      local a = 0
      func = function(inc)
        a = a + inc
        return a
      end
    end
    
  2. Use a global function:

    do
      local a = 0
      function func(inc)
        a = a + inc
        return a
      end
    end
    
  3. Use a method:

    local tbl = {}
    do
      local a = 0
      function tbl:func(inc)
        a = a + inc
        return a
      end
    end
    
在这三种情况下,您仍然可以在块关闭后调用func(),但是a只能从该函数中visible,而不是任何其他地方。

14
除了已经给出的好回答之外,我想提到在两个或多个函数之间共享私有变量的能力:
do

  local i = 0

  function inc()
    i = i + 1
    return i
  end

  function dec()
    i = i - 1
    return i
  end

end

如果在 do 块之前将函数名定义为局部变量,但是没有值,这对于局部函数也是有效的。 - Lazerbeak12345

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接