2010-07-02 52 views
3

考慮下面的實施的功能的計算階乘:[1]爲什麼'let`不能用於命名內部遞歸過程?

(define fac-tail 
    (lambda (n) 
    (define fac-tail-helper 
     (lambda (n ac) 
     (if (= 0 n) 
      ac 
      (fac-tail-helper (- n 1) (* n ac))))) 
    (fac-tail-helper n 1))) 

我試圖使用let用於內重寫定義:

(define fac-tail-2 
    (lambda (n) 
    (let ((fac-tail-helper-2 
      (lambda (n ac) 
       (if (= 0 n) 
        ac 
        (fac-tail-helper-2 (- n 1) (* n ac)))))) 
    (fac-tail-helper-2 n 1)))) 

有一個在define時間沒有錯誤,但執行結果爲:

#;> (fac-tail-2 4) 
Error: undefined variable 'fac-tail-helper-2'. 
{warning: printing of stack trace not supported} 

如何使let版本正常工作?

方案版本是SISC v 1.16.6

[1]基於對factorial在節中的迭代版本SICP 1.2.1 http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-11.html#%_sec_1.2.1

+0

很高興知道有人在那裏攻擊計劃...... :) – galambalazs 2010-07-02 22:26:28

回答

8

我怎樣才能讓咱們版本的工作?

使用letrec代替let

+0

本質上的區別是,letrec'd過程的主體可以引用自身,而let'd過程的主體不能。 – erjiang 2010-07-03 17:45:22

6

R.肯特Dvbvig說:


事實上,一個let表達式是一個句法 擴展定義的lambda和過程應用程序,其中 都是核心句法形式。通常,

(let ((var expr) ...) body1 body2 ...) 

的任何表達式等同於以下內容。

((lambda (var ...) body1 body2 ...) 
expr ...)" [1] 

這意味着fac-tail-2等同於:

(define fac-tail-2 
    (lambda (n) 
    ((lambda (fac-tail-helper-2) 
     (fac-tail-helper-2 n 1)) ;; <== scope where fac-tail-helper-2 is visible. 
    (lambda (n ac) ;; this lambda is assigned to fac-tail-helper-2 
     (if (= 0 n) 
      ac 
      (fac-tail-helper-2 (- n 1) (* n ac))))))) ;; <=== problem 

它變得清晰,問題是fac-tail-helper-2名稱可見作爲 paramenter在lambda的身體上面強調,但在lambda內的名稱不是 ,它被分配給參數fac-tail-helper-2

[1]第2.5節 「Lambda表達式」 的Scheme編程語言,第4版http://scheme.com/tspl4/start.html#SECTGSLAMBDA

0

兩個其他的替代品,爲了完整性而添加。

Scheme編程語言,第4版 3.2節對使用let和遞歸功能的其他兩個備選方案。 http://scheme.com/tspl4/further.html#./further:h2

第一個是聰明的,不推薦。它包括向lambda調用一個參數,它是一個lambda函數,然後傳入自身以啓動everthing。

(define fac-tail-4 
    (lambda (n) 
    (let ((fac-tail-helper 
      (lambda (fac-tail-helper n ac) 
       (if (= 0 n) 
        ac 
        (fac-tail-helper fac-tail-helper (- n 1) (* n ac)))))) 
     (fac-tail-helper fac-tail-helper n 1)))) 

和更簡單的是一個名爲let這可以使用letrec INSEAD單遞歸。

(define fac-tail-3 
    (lambda (x) 
    (let fac-tail-helper ((n x) (ac 1)) 
     (if (= 0 n) 
      ac 
      (fac-tail-helper (- n 1) (* n ac)))))) 

儘管該版本隱藏了與fac-tail-helper綁定的隱式lambda定義。