簡體   English   中英

有趣的遞歸lambda示例

[英]Interesting recursive lambda example

我偶然發現了一個有趣的遞歸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張! 正如人們所期望的那樣。

因此,在這種情況下,“內部rec”(在函數主體中)綁定到功能名稱的rec,而不是查看rec變量,並且變量rec的重新分配不會改變行為。

因此,我真正要問的是這些語言決定綁定lambda變量的機制。

我自己為一個班級項目編寫了一個解釋器,並且遇到了相同的問題,即何時何地綁定這些變量。 因此,我想了解它如何在流行語言中實現相似的功能。

您可以添加一些console.log並查看,首先用10調用f ,然后用9調用rec ,結果為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)); 

保持rec

 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)); 

我將為python解決這個問題,因為這是我所熟悉的。

首先,這種行為僅是可能的,因為python(並且我認為它類似於javascript)遵循其綁定的后期綁定。 進一步閱讀

后期綁定是在運行時查找閉包中的名稱的方式(不同於早期綁定,其中在編譯時查找名稱)。

這允許通過重新綁定在運行時查找的變量(例如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后續函數調用將使用在函數調用中查找的最新記錄。

f(10) #100

PS。 完整的代碼添加如下:

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

這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

我建議您正確使用變量名,在這里您不需要重新分配

為什么使用第二條記錄而不是第一條記錄?

好吧,您的函數調用是在rec分配后發生的,因此您在rec中擁有最新的值

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)); 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM