2013-03-06 70 views
3

我通過這本書Structure and implementation of computer programs工作,並在其中一章有用來計算一個數的階乘一些代碼:定義一個函數內聯與否有什麼區別?

(define (factorial n) 
    (fact-iter 1 1 n)) 

(define (fact-iter product counter max-count) 
    (if (> counter max-count) 
     product 
     (fact-iter (* counter product) 
        (+ counter 1) 
        max-count))) 

在本書中,我瞭解到,我可以用另一種定義內置函數的早期像這樣的功能:

(define (factorial n) 
    (define (fact-iter product counter max-count) 
    (if (> counter max-count) 
     product 
     (fact-iter (* counter product) 
        (+ counter 1) 
        max-count))) 
    (fact-iter 1 1 n)) 

我知道,使用第二種方法fact-iterfactorial範圍之外將無法訪問,但我想知道,當我運行的factorial第二個版本到底發生了什麼?

一個新的本地的符號fact-iter綁定都定義和創建一個新的功能或程序編譯時,這種結合創造了一次?

我從Java背景的,這是目前尚不清楚對我來說。

+0

我忘了計劃謝謝。 – 2013-03-06 14:59:16

回答

4

它取決於方案實施(在SICP的後續章節中討論了其策略)。從概念上講,每次調用factorial時,都會在第二個定義中定義/編譯一個新函數。然而,一個好的編譯器可以轉換這些代碼,使它更像你的第一個定義。

由於此構造在Scheme中很常見(編寫循環的慣用方式是命名 - let構造,其中也定義了函數),因此Scheme編譯器應該非常擅長執行此優化。而事實上,你的榜樣是很容易辦理的優化,因爲內部函數的定義實際上並不依賴於外部函數結合的任何變量,因此它可以被淘汰幾乎原樣解除(只有名稱可能要改變) 。

+0

我正在使用R5RS。 – 2013-03-06 15:03:31

+0

@AdamArold:這不是一個計劃的實施,這是語言標準的一個版本。 – 2013-03-06 15:04:00

+0

我明白了。談論lisp時很容易混淆。那麼Racket是一個實現嗎? – 2013-03-06 15:07:55

2

每次調用階乘函數時,函數編譯爲的情況絕不是這樣。函數在形式上是一段代碼和一個環境;每次通話都會改變環境。例如:

(define (find x l) 
    (define (equal-to-x y) (equal? x y)) 
    ....) 

在上面,每次調用'find'都會產生一個新的函數'equal-to-x'。 'equal-to-x'函數的'環境'引用另一個作用域中定義的變量'x'。然而,適當好的編譯器可能會注意到,等於對X永遠不會返回或結合到外的範圍的變量,因此,編譯器可以「內聯」的代碼 - 從而不需要一個新的功能(代碼+環境)。

在代碼中,遊離的引用(+,*,及>)的內部定義實況ITER(您的第二種情況)的全部全局定義的並且不返回實況ITER函數(或結合)。因此,一個足夠好的編譯器會嵌入它。

這裏是一個不太好編譯器的情況。你可以在'find'的反彙編中看到一個函數/閉包(代碼+環境)被創建,並且在反編譯'ex'中使用了一個環境引用(在'x'處)。

=> (define find (lambda (x l) 
    (define ex (lambda (y) (= x y))) 
    (ex (car l)))) 
(=>) 
=> (sys:disassemble find) 
;; Address : 0x00327e90 
;; Label : find 
;; Constants: 
;;   0: #t 
;;   1: #[<code> ex 1] 
;;   2: (<cons> 6) 
;; Code  : 
;;  0-1: explode 2 
;;  2-3: check 2 
;;  4-5: get-loc 0 
;;  6-8: closure 1, 1 
;;  9-10: get-loc 2 
;;  11-13: get-loc-res 1, 2 
;;  14: cons$car 
;;  15-16: call-tail-check 1 
;;   : 
;; Address : 0x0031fb40 
;; Label : ex 
;; Constants: 
;;   0: #t 
;;   1: (<number> 1 142 42 154 158) 
;; Code  : 
;;  0-1: explode 1 
;;  2-3: check 1 
;;  4-6: get-env-res 0, 1 
;;  7-9: get-loc-res 0, 1 
;;  10: number$= 
;;  11-12: return 1 
;;   : 
;; Env  : 
#[<function> find 2] 
+0

你的第一句話有點令人困惑;我認爲你最初的意思恰恰相反。 – leppie 2013-03-06 15:19:32

+0

如何在球拍中反彙編我的功能?你的例子中使用了什麼實現? – 2013-03-06 15:35:42

+0

這是我的Scheme-like編譯器(這就是爲什麼我說'不是很好的編譯器')。在球拍,我不確定,但嘗試(apropos「反彙編」) – GoZoner 2013-03-06 15:38:25

相關問題