在使用Lua使用闭包和递归从树形 struct 生成字符串时,我发现了一个意外的行为.

我将代码简化为这样,并制作了一个JS版本.JS版本的工作与预期一样,但我不明白为什么,也不知props 体是怎么回事.

代码是:

function union(children)
    local union = {}
    function union:method(p)
        print("union method", p, #children)

        -- rename p2 to p to make it work as expected
        function f(p2, children)
            print("union f", p, #children)
            local result = nil
            if #children == 1 then
                result = children[1]:method(p)
            elseif #children == 2 then
                result = children[1]:method(p) .. children[2]:method(p) 
            else
                local first_child = table.remove(children)
                result = first_child:method(p) .. f(p, children)
            end
            return result
        end
        return f(p, children)
    end        
    return union
 end

 function transform(children)
    local child = children[1]
    local transform = {}
    function transform:method(p)
        print("transform start")
        res = child:method(p .. "TRANSFORMED  ")
        print("transform end")
        return res
    end        
    return transform
 end

 function leaf()
    local leaf = {}
    function leaf:method(p)
        return p
    end
    return leaf
 end

root = union({leaf(), leaf(), transform({union({leaf()})}) })
print(root:method("p"))

输出为:"pTRANSFORMED"

但我预计:"pTRANSFORMED pp"

总之,我不明白为什么"转换" node 会影响三片叶子,而不是只影响一片叶子.

JS代码为:

function union(children){
    const union = {}
    union.getField = (p) =>{
        function f(p2, children){
            console.log("union", p, p2, children.length)
            let result = null
            if (children.length == 1 ){
                result =  children[0].getField(p)
            }else if ( children.length == 2 ){
                result = children[0].getField(p) + children[1].getField(p) 
            }else {
                const first_child = children.pop()
                result = first_child.getField(p) + f(p, children) 
            }
            return result
        }
        return f(p, children)
    }      
    return union
}

 function transform(children){
    const child = children[0]
    const transform = {}
    transform.getField = (p)=>{
        return child.getField(p + "TRANSFORMED  ")
    }      
    return transform
 }

 function leaf(){
    const leaf = {}
    leaf.getField = (p) =>{
        return p
    }
    return leaf
 }

const root = union([leaf(), leaf(), transform([union([leaf()])]) ])
console.log(root.getField("p"))

推荐答案

函数f的词法范围不限于每个union:method.

function foo()
end

相当于

foo = function ()
end

每次调用union:method时,函数f都会在块的词法作用域中重新定义.

这将在上次重新定义时的上限值p附近创建闭合.

以下几条打印声明揭示了这一点:

function union(children)
    local union = {}

    function union:method(p)
        print("union method", p, #children)

        function f(p2, children)
            -- ...
            print('f<p>', p)
            -- ...
        end

        print('f redefined', f)

        return f(p, children)
    end

    return union
end
union method    p   3
f redefined function: 0x5652063c80c0
f<p>    p
union method    pTRANSFORMED    1
f redefined function: 0x5652063c8780
f<p>    pTRANSFORMED
f<p>    pTRANSFORMED
pTRANSFORMED  pTRANSFORMED  pTRANSFORMED

root:method("p")会使f的值接近p作为"p",但调用的第一个子方法是转换后的联合,

local first_child = table.remove(children)
result = first_child:method(p) .. f(p, children)

这导致f的重新定义围绕其变元p .. "TRANSFORMED"结束.伪递归调用紧随其后,执行

result = children[1]:method(p) .. children[2]:method(p)

其中错误的值为p.

p2更改为p会通过参数将正确的值带回作用域,而不是因不正确的UP值而被忽略.在这一点上,该函数没有封闭任何东西,实际上可以完全放置在union的定义之外.

或者,将每个函数的词法作用域限定到每个union:method会导致它创建正确的闭包,

local function f(_, __)

不需要任何争论就能证明.

union method    p   3
f redefined function: 0x55b789c4f0a0
f<p>    p
union method    pTRANSFORMED    1
f redefined function: 0x55b789c4f7a0
f<p>    pTRANSFORMED
f<p>    p
pTRANSFORMED  pp

在JavaScript中,函数声明

function f(p2, children) {

}

在其封闭的块内按词法作用域(并提升).

Javascript相关问答推荐

是什么原因导致此Angular 16应用程序中类型错误时属性结果不存在?

调用removeEvents不起作用

如何在Angular中插入动态组件

为什么ngModel不能在最后一个版本的Angular 17上工作?'

我们如何从一个行动中分派行动

如何从URL获取令牌?

如何强制Sphinx中的自定义js/css文件始终加载更改而不缓存?

Next.js服务器端组件请求,如何发送我的cookie token?

更改预请求脚本中重用的JSON主体变量- Postman

使用Nuxt Apollo在Piniastore 中获取产品细节

如何在Node.js中排除导出的JS文件

使用auth.js保护API路由的Next.JS,FETCH()不起作用

如何访问此数组中的值?

JavaScript将字符串数字转换为整数

我为什么要使用回调而不是等待?

我怎样才能点击一个元素,并获得一个与 puppeteer 师导航页面的URL?

如何向内部有文本输入字段的HTML表添加行?

响应,Use Callback更新状态变量,该变量也存在于其依赖数组中,它如何防止无限重新呈现?

我如何让我的弹力球在JavaScript和HTML画布中相互碰撞后改变 colored颜色 ?

如何导入我在Web Worker创建的函数?