在Lua中,把嵌套函数设为全局函数有什么意义?

编程语言 2026-07-10

这是Knuth的“Man/Boy Test”的Lua版本。为了复现 维基百科的条目 中显示的结果,我发现必须把嵌套的函数B 声明为local。我不明白这是为什么。

-- Knuth's Man-Boy test for ALGOL60

-- but I want to understand the critical matter of "local" in front
-- of the inner function

ftab = {} ;
ftab.n = 0 ;

function fs( f )
  local s ;

  s = tostring( f ) ;
  s = s:sub( 10 ) ;

  if ftab[f] == nil then
    ftab.n = ftab.n + 1 ;
    ftab[f] = string.format( "f%03d", ftab.n ) ;
  end ;

  s = " " .. ftab[f] ;
  return s ;
end ; -- fs

function is( n )
  local s ;
  s = string.format( "%3d", n ) ;
  return s ;
end ; -- is

function A(k,x1,x2,x3,x4,x5)
  print( "  A" .. fs(A) .. is(k) .. fs(x1) .. fs(x2) .. fs(x3) .. fs(x4)
         .. fs(x5), _G, _ENV ) ;

  --local
  function B()
    print( "  B" .. fs(B) .. is(k) .. fs(x1) .. fs(x2) .. fs(x3) .. fs(x4)
           .. fs(x5), _G, _ENV ) ;
    k = k - 1 ;
    return A( k, B, x1, x2, x3, x4 ) ;
  end ; -- B

  local v ; 
  if k <= 0 then v = x4() + x5() ; else v = B() ; end ;
  return v ;
end ; -- A

function K(n)
  function Kn()
    return n ;
  end ; -- Kn
  return Kn ;
end ; -- K

print( "A    = ", A ) ;
print( "B    = ", B ) ;
print( "_G   = ", _G ) ;
print( "_ENV = ", _ENV ) ;

print( "A -> ", A( 5, K(1), K(-1), K(-1), K(1), K(0) ) ) ;

print( "A    = ", A ) ;
print( "B    = ", B ) ;
print( "_G   = ", _G ) ;
print( "_ENV = ", _ENV ) ;
A    =  function: 00dfae58       f001
B    =  nil      fnil
_G   =  table: 00df2d78
_ENV =  table: 00df2d78
  A f001  5 f002 f003 f004 f005 f006
  B f007  5 f002 f003 f004 f005 f006
  A f001  4 f007 f002 f003 f004 f005
  B f008  4 f007 f002 f003 f004 f005
  A f001  3 f008 f007 f002 f003 f004
  B f009  3 f008 f007 f002 f003 f004
  A f001  2 f009 f008 f007 f002 f003
  B f010  2 f009 f008 f007 f002 f003
  A f001  1 f010 f009 f008 f007 f002
  B f011  1 f010 f009 f008 f007 f002
  A f001  0 f011 f010 f009 f008 f007
  B f012  3 f007 f002 f003 f004 f005

在调用函数A 之前,B为 nil——即使B 是全局变量也一样。A第一次返回后,如果B 是local,那么B(那个不存在的全局变量)再次为nil。

我搞不懂的是,B的全局性为什么会影响执行?使用“local function B”时,最终结果是“0”;不使用“local”时,最终结果是“1”。

我追溯到A 第一次调用且k < 1的情形。对B 的下一次调用并不能将自身识别为A 的x4参数,尽管它“看见”了应该对x4参数可见的A 的参数。

这时我就放弃去设想可能的原因。有人把我从痛苦中解救出来了。

解决方案

我看了维基百科的那篇文章和你的代码,完全不知道那个算法在做什么,不过我确实知道Lua函数是怎么定义的。

函数可以是局部变量,也可以是全局变量;当你在A 函数内部定义B 函数时,如果在定义前不加local,那么B 就会成为一个全局变量。

另外,还有上值(upvalues)。每当Lua的控制流经过一个函数定义时,就会为该函数创建一个新的闭包,并能够访问在该闭包中捕获的局部变量(若有的话)。(更多信息: https://www.lua.org/pil/6.1.html)

在你的代码中,每次调用A 时,如果把B 定义为全局函数,那么全局变量B 会被新的闭包覆盖。于是对B 的后续调用就会执行B 的最新定义。

但如果把B 定义为局部函数,它就不会覆盖B 之前的闭包。于是对B 的后续调用取决于所引用的是哪一个B 的闭包。

另外还要注意的是,如果一个变量是全局的,它永远不会成为上值(因为全局变量只存在于单一的 _G表中,而局部变量可以在每个闭包中独立存在)。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章