两个相似定义之间的差异
Differences between two similar definitions
和
有区别吗
(define make-point cons)
和
(define (make-point x y)
(cons x y))
?
一种比另一种更有效,还是完全等效?
在语义上它们是等价的:make-point
将 cons
两个元素。但是第一个是创建 cons
函数的 别名 ,而第二个是定义一个简单调用 cons
的新函数,因此它将是稍微慢一点,但是额外的开销可以忽略不计,如果编译器好的话甚至不存在。
对于cons
,你的两个版本没有区别。
对于像 +
这样的可变过程,+
和 (lambda (x y) (+ x y))
之间的区别在于后者将过程限制为只能使用两个参数调用。
出于好奇,我做了一个快速而肮脏的实验。似乎只是别名 cons
的速度几乎是将其包装在新函数中的两倍。
(define mk-point cons)
(define (make-point x y)
(cons x y))
(let ((start (current-inexact-milliseconds)))
(let loop ((n 100000000))
(mk-point 10 10)
(if (> n 0)
(loop (- n 1))
(- (current-inexact-milliseconds) start))))
(let ((start (current-inexact-milliseconds)))
(let loop ((n 100000000))
(make-point 10 10)
(if (> n 0)
(loop (- n 1))
(- (current-inexact-milliseconds) start))))
;;; Result
4141.373046875
6241.93212890625
>
运行 在 Xubuntu 上的 DrRacket 5.3.6 中。
这里有几个不同的问题。
As , one is an indirection, and one is a wrapper. 并指出,如果不进行优化,间接寻址所花费的时间可能是间接寻址的两倍。那是因为别名使得两个变量的 value 成为 same 函数。当系统评估 (cons …) 和 (make-point …) 它评估变量 cons和 make-point 并返回 same 函数。在间接版本中,make-point 和 cons 是 not 相同的函数。 make-point 是一个 new 函数,它再次调用 cons。那是 两次 次函数调用,而不是一次。所以速度 可能 是一个问题,但一个好的优化编译器可能能够使差异忽略不计。
但是,如果您以后能够更改这两个变量中的任何一个的值,那么就会有一个非常的重要区别。当您评估 (define make-point kons) 时,您评估变量 kons once 并设置make-point 的值到您在评估时获得的 one 值。当您评估 (define (make-point x y) (kons x y)) 时,您将 make-point 的值设置为新的功能。每次调用该函数时,都会评估变量 kons,因此会反映对变量 kons 的任何更改。让我们看一个例子:
(define (kons x y)
(cons x y))
(display (kons 1 2))
;=> (1 . 2)
现在,让我们写一个间接寻址和一个别名:
(define (kons-indirection x y)
(kons x y))
(define kons-alias kons)
这些现在产生相同的输出:
(display (kons-indirection 1 2))
;=> (1 . 2)
(display (kons-alias 1 2))
;=> (1 . 2)
现在让我们重新定义 kons 函数:
(set! kons (lambda (x y) (cons y x))) ; "backwards" cons
作为 wrapper 围绕 kons 的函数,即间接,看到 的新值kons,但别名没有:
(display (kons-indirection 1 2))
;=> (2 . 1) ; NEW value of kons
(display (kons-alias 1 2))
;=> (1 . 2) ; OLD value of kons
和
有区别吗(define make-point cons)
和
(define (make-point x y)
(cons x y))
?
一种比另一种更有效,还是完全等效?
在语义上它们是等价的:make-point
将 cons
两个元素。但是第一个是创建 cons
函数的 别名 ,而第二个是定义一个简单调用 cons
的新函数,因此它将是稍微慢一点,但是额外的开销可以忽略不计,如果编译器好的话甚至不存在。
对于cons
,你的两个版本没有区别。
对于像 +
这样的可变过程,+
和 (lambda (x y) (+ x y))
之间的区别在于后者将过程限制为只能使用两个参数调用。
出于好奇,我做了一个快速而肮脏的实验。似乎只是别名 cons
的速度几乎是将其包装在新函数中的两倍。
(define mk-point cons)
(define (make-point x y)
(cons x y))
(let ((start (current-inexact-milliseconds)))
(let loop ((n 100000000))
(mk-point 10 10)
(if (> n 0)
(loop (- n 1))
(- (current-inexact-milliseconds) start))))
(let ((start (current-inexact-milliseconds)))
(let loop ((n 100000000))
(make-point 10 10)
(if (> n 0)
(loop (- n 1))
(- (current-inexact-milliseconds) start))))
;;; Result
4141.373046875
6241.93212890625
>
运行 在 Xubuntu 上的 DrRacket 5.3.6 中。
这里有几个不同的问题。
As
但是,如果您以后能够更改这两个变量中的任何一个的值,那么就会有一个非常的重要区别。当您评估 (define make-point kons) 时,您评估变量 kons once 并设置make-point 的值到您在评估时获得的 one 值。当您评估 (define (make-point x y) (kons x y)) 时,您将 make-point 的值设置为新的功能。每次调用该函数时,都会评估变量 kons,因此会反映对变量 kons 的任何更改。让我们看一个例子:
(define (kons x y)
(cons x y))
(display (kons 1 2))
;=> (1 . 2)
现在,让我们写一个间接寻址和一个别名:
(define (kons-indirection x y)
(kons x y))
(define kons-alias kons)
这些现在产生相同的输出:
(display (kons-indirection 1 2))
;=> (1 . 2)
(display (kons-alias 1 2))
;=> (1 . 2)
现在让我们重新定义 kons 函数:
(set! kons (lambda (x y) (cons y x))) ; "backwards" cons
作为 wrapper 围绕 kons 的函数,即间接,看到 的新值kons,但别名没有:
(display (kons-indirection 1 2))
;=> (2 . 1) ; NEW value of kons
(display (kons-alias 1 2))
;=> (1 . 2) ; OLD value of kons