【问题标题】:Global and local recursive functions in lualua中的全局和局部递归函数
【发布时间】:2018-06-20 22:10:59
【问题描述】:

我对 lua 很陌生,我想了解以下行为。

当我尝试运行以下递归函数时:

local func = function ( n )
  if n == 1 then return 1
  else return n * func( n - 1 )
  end
end

print( func( 5 ) )

程序将失败并出现错误:

lua: main.lua:16: attempt to call a nil value (global 'func')
stack traceback:
main.lua:16: in local 'func'
main.lua:38: in main chunk
[C]: in ?

没关系,因为根据explanationfunc 变量的本地版本尚不清楚,因此它会尝试调用全局版本。但是当我删除 local 关键字时,下面的代码可以正常工作吗?

func = function ( n )
  if n == 1 then return 1
  else return n * func( n - 1 )
  end
end

print( func( 5 ) )

程序打印 120 作为结果,但全局 func 之前从未初始化或使用过。这怎么可能,这也不会引发错误?第二个例子不是像第一个例子那样引用全局 func 吗?

【问题讨论】:

    标签: variables recursion lua scripting-language


    【解决方案1】:

    第二个例子不是像第一个例子那样引用全局函数吗?

    它是:)

    你看,当你编译新函数时,值是否存在并不重要。它不检查存储在func 变量中的值,而是在每次调用时查找它。唯一重要的是变量的可见性。如果变量在本地范围内不可见,则将其视为全局变量,并将在全局环境表中查找。更多细节参见 Lua 手册,章节3.5 – Visibility Rules

    第一个示例不起作用,因为在计算完整表达式之前,local func 变量不可见。这就是第一个示例中断的原因,它试图调用缺少的全局变量。

    如果您希望该 func 是本地的,请声明该变量,然后 然后 分配它。喜欢:

    local func
    func = function ( n )
      if n == 1 then return 1
      else return n * func( n - 1 )
      end
    end
    
    print( func( 5 ) )
    

    在这种情况下使用 Lua 的语法糖可能会更容易:

    local function func( n )
      if n == 1 then return 1
      else return n * func( n - 1 )
      end
    end
    
    print( func( 5 ) )
    

    它将被完全翻译为声明变量的相同序列,然后分配它。

    这次func 变量将对新的function(n) 可见,因此它将读取值以从该特定上值调用。

    请注意,稍后仍可以通过将不同的内容分配给func 变量来“破坏”该函数。 Lua 中的函数(与任何其他值一样)没有名称,只有变量有。因此对函数的调用不是硬编译的,要调用的函数值总是在每次调用之前从变量中获取。

    【讨论】:

    • 我现在很清楚了。因此,当调用本地 func() 时,内部 func() 仍然引用它的全局(未初始化)版本,即使在调用时本地 func() 已经存在。刚刚通过声明全局 func() 和本地版本,同时在其中打印一些帮助消息来“直观地”验证这一点。一开始并没有这样想。谢谢你的回答。
    【解决方案2】:

    @Vlad answers 这个问题很好。为了清楚起见,关于这个问题的几点:

    1. 函数是值,因此如果没有问题的上下文,标题中的“全局函数”本身就没有意义。

    2. 变量绑定是编译器将代码中的标识符映射到变量的过程。关于变量绑定的问题通常与递归函数有关,但绑定规则更简单、更通用。它们与递归、函数定义甚至本地与全局无关。尽管如此,函数定义语句的语法糖可能会混淆问题。

    例如:

    local x = x + 1 
    

    表达式中的x 不是正在创建的x。它是哪个变量取决于上面的任何代码。它可以是在语句块中声明的局部变量,也可以是函数参数。这样的声明可以在同一个块或函数定义中,或者在外部的above中。全局只是后备。

    显然,如果在上面的示例中没有使用local,相同的名称将绑定到相同的变量。

     y = y + 1
    

    y 的两个名称都是同一个变量,但如上所述,哪个变量由其他代码确定。

    这个函数定义语句类似:

     function f() end
    

    如果本地 f 在范围内,则该变量是编译器将“f”绑定到的变量,否则为全局环境中的“f”。

    【讨论】:

      猜你喜欢
      • 2012-08-08
      • 2014-01-31
      • 1970-01-01
      • 2016-02-01
      • 2017-02-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多