定义内联函数有什么区别?

时间:2013-03-06 14:52:17

标签: binding lisp scheme

我正在阅读这本书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-iter将无法在factorial范围之外访问,但我想知道当我运行第二版factorial时会发生什么?

是否定义了符号fact-iter的新本地绑定并创建了一个新函数,或者只在程序编译时创建了一次此绑定?

我来自java背景,这对我来说还不清楚。

2 个答案:

答案 0 :(得分:4)

它取决于Scheme实现(其策略将在 SICP 的后续章节中讨论)。从概念上讲,根据您的第二个定义,每次调用factorial时都会定义/编译一个新函数。但是,一个好的编译器可以转换这个代码,使它更像你的第一个定义。

由于这个结构在Scheme中是如此常见(编写循环的惯用方法是命名 - let构造,它也可以动态定义函数),因此Scheme编译器应该非常擅长这种优化。事实上,你的例子对于优化器来说很容易处理,因为内部函数的定义实际上并不依赖于外部函数绑定的任何变量,所以它几乎可以原样解除(只有名称可能需要更改)

答案 1 :(得分:2)

每次调用factorial时,新函数都不会编译。功能正式是一段代码和环境;每次通话都可以改变环境。例如:

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

在上面,每次调用'find'都会产生一个新的函数'equal-to-x'。 'equal-to-x'函数的'environment'引用了另一个范围中定义的变量'x'。但是,一个合适的编译器可能会注意到,从不返回等于x或绑定到超出范围的变量,因此编译器可能“内联”代码 - 从而不需要新的函数(代码+环境)。

在您的代码中,内部定义的事实(第二种情况)的自由引用(+,*和>)都是全局定义的,并且不返回(或绑定)事实 - 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]