分享
三行代码  ›  专栏  ›  技术社区  ›  guser

有趣的递归lambda示例 - Interesting recursive lambda example

  •  -1
  • guser  · 技术社区  · 2 月前

    我刚刚偶然发现了一个有趣的递归lambda的例子,我不太明白它为什么会以这种方式工作。

    rec = lambda x : 1 if x==0 else rec(x-1)*x
    f = rec 
    rec = lambda x: x+1
    print(f(10))  
    

    在javascript中也一样。

    var rec = function(a) {
      if (a == 0) return 1;
      return rec(a - 1) * a;
    }
    var f = rec
    rec = function(a) {
      return a + 1;
    }
    console.log(f(10));

    令我惊讶的是,这两种印刷品都是100而不是10!(如我所料)。

    为什么重新分配rec会改变f函数的行为?当在lambda中捕获rec变量时,它不是引用lambda本身吗?


    编辑。 由于大多数答案解释了正在发生的事情,让我重新表述这个问题,因为我正在寻找更深入的解释。

    所以在第一行中声明函数rec的时候,为什么函数体中的rec不绑定到自身?

    例如,如果您使用javascript,并按照您得到的答案之一中建议的“相同”方式重写第一行:

    var rec =function rec(a) {
        if (a == 0) return 1;
        return rec(a - 1) * a;
    };
    f = rec;
    
    rec = function (a) {
        return a + 1;
    }
    
    console.log(f(10));
    

    这张印10张!如人们所料。

    因此,在这种情况下,“inner rec”(在函数体中)绑定到函数名的rec,而不是查看rec变量,变量rec的重新分配不会改变行为。

    所以我真正要问的是,这些语言决定如何绑定lambda中的变量的机制。

    我自己为一个类项目编写了一个解释器,我遇到了同样的问题,即何时何地绑定这些变量。所以我想了解这是如何在流行语言中实现类似的东西的。

    4 回复  |  直到 2 月前
        1
  •  2
  •   Paritosh Singh    2 月前

    我将为python处理它,因为这是我熟悉的。

    首先,这种行为是可能的,因为python(并且看起来像我假定的javascript)为其闭包遵循后期绑定。 further read

    后期绑定是在运行时查找闭包中的名称(与在编译时查找名称的早期绑定不同)。

    这允许在运行时通过重新绑定在运行时查找的变量(如rec等函数)来改变行为。

    最后一步就是把lambda函数转换成等价的 def 语法使实际行为更清晰。

    代码:

    rec = lambda x : 1 if x==0 else rec(x-1)*x
    f = rec 
    rec = lambda x: x+1
    print(f(10)) 
    

    可以等同于:

    第一:

    def somefunc(x):
        return 1 if x==0 else rec(x-1)*x 
    

    注释 ,python不会抱怨即使在干净的会话/内核上也不存在rec,因为它不会在函数定义期间查找值。后期绑定意味着除非调用这个函数,否则python不关心rec是什么。

    然后:

    rec = somefunc
    f = rec
    
    def someotherfunc(x):
        return x + 1
    
    f(10) #3628800
    

    现在我们改变 rec 功能

    rec = someotherfunc
    

    并观察随后的函数调用 f 将使用后期绑定的rec,即在函数调用上查找的rec。

    f(10) #100
    

    补充完整代码如下:

    def somefunc(x):
        return 1 if x==0 else rec(x-1)*x
    
    rec = somefunc
    
    f = rec
    
    def someotherfunc(x):
        return x + 1
    
    f(10) #3628800
    
    rec = someotherfunc
    
    f(10) #100
    
        2
  •  2
  •   Nina Scholz    2 月前

    你可以加一些 console.log 先看看 f 用调用 10 然后 rec 具有 9 结果是 10 * 10 .

    var rec = function(a) {
            console.log('f', a);
            if (a == 0) return 1;
            return rec(a - 1) * a;
        };
        f = rec;
    
    rec = function(a) {
        console.log('rec', a);
        return a + 1;
    }
    
    console.log(f(10));

    保持 录制 .

    var rec = function rec(a) {
            console.log('f', a);
            if (a == 0) return 1;
            return rec(a - 1) * a;
        };
        f = rec;
    
    rec = function(a) {
        console.log('rec', a);
        return a + 1;
    }
    
    console.log(f(10));
        3
  •  0
  •   Sanjay Bisht    2 月前

    这3条语句可以概括为一条语句

    rec = lambda x : 1 if x==0 else rec(x-1)*x
    f = rec 
    rec = lambda x: x+1
    

    从1&2

    f = lambda x : 1 if x==0 else rec(x-1)*x
    

    从上面开始&3

    f = lambda x : 1 if x==0 else x*x
    
        4
  •  0
  •   Code Maniac    2 月前

    我建议正确使用变量名,这里不需要重新分配

    为什么使用第二个记录而不是第一个记录?

    好吧,函数调用是在重新分配rec之后发生的,所以在rec as中有最新的值

    rec = function(a) {
      return a + 1;
    }
    

    var f = function(a) {
      if (a == 0) return 1;
      return rec(a - 1) * a;
    }
    
    var rec = function(a) {
      return a + 1;
    }
    console.log(f(10));