lua中的全局和局部递归函数

问题描述 投票:0回答:2

我对 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 ?

没关系,因为根据解释func变量的本地版本尚不清楚,因此它尝试调用全局变量。但是当我删除 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
2个回答
19
投票

第二个示例不是像第一个示例一样引用全局 func 吗?

是:)

你看,当你编译新函数时,值存在与否并不重要。 Lua 在编译时不会检查

func
变量中存储的值,而是每次调用时 Lua 都会查找它。编译期间唯一重要的是变量的可见性。如果变量在局部范围内不可见,则它被视为全局变量,并将在全局环境表中查找。有关更多详细信息,请参阅 Lua 手册,第 3.5 – 可见性规则

第一个示例不起作用,因为在计算完整的表达式之前,

local 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)
可见,因此它将读取要从该特定 upvalue 调用的值。

请注意,稍后仍然可以通过向

func
变量分配不同的内容来“破坏”该函数。 Lua 中的函数(和任何其他值一样)没有名称,只有变量有名称。因此,对函数的调用不是硬编译的,要调用的函数值始终在每次调用之前从变量中获取。


2
投票

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

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

  2. 变量绑定是编译器将代码中的标识符映射到变量的过程。关于变量绑定的问题通常会出现在递归函数中,但绑定规则更简单、更通用。它们与递归、函数

    定义甚至本地与全局没有任何关系。尽管如此,函数定义的语法糖语句可能会混淆问题。

例如:

local x = x + 1

表达式中的 

x

 并不是正在创建的 
x
。它是哪个变量取决于上面的任何代码。它可以是在语句块中声明的局部变量或作为函数参数。这样的声明可以位于同一块或函数定义中,或者位于外部块或函数定义中。全球只是后备方案。
显然,如果上面的示例中没有使用 local

,则相同的名称将绑定到相同的变量。

 y = y + 1

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

这个函数定义语句类似:

function f() end

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

	

© www.soinside.com 2019 - 2024. All rights reserved.