我正在玩球拍/方案,它允许我重新定义实例define
并将其绑定为值。
> (define define 2)
> define
2
在该范围内,我无法再使用define
定义任何内容,因为它显然已绑定到2.这适用于我尝试过的所有“关键字”(if
,cond
等)。
但是,无法使用define
指定我自己的定义函数:
> (define mydef define)
stdin::14: define: not allowed in an expression context in: define
=== context ===
/usr/share/racket/collects/racket/private/norm-define.rkt:8:4: normalize-definition
/usr/share/racket/collects/racket/private/kw.rkt:796:2
/usr/share/racket/collects/racket/private/misc.rkt:87:7
我想如果我愿意,还有另一种方法可以在球拍中扩展语言以添加我自己的定义功能,但为什么这种方式不被允许?
这确实让我想知道是否有任何有效的用例来重新定义define
?我意识到这有点基于意见,但我正在寻找这个可能可能是合理的事情的用例(无论是否是另一回事)。
答案 0 :(得分:5)
是的,您实际上可能希望扩展define
表单以提供标准define
不具备的功能。一个例子是提供装饰器(感谢uselpa的灵感答案):
(require (only-in racket/base (define basic-define)))
(define-syntax wrap-decorators
(syntax-rules ()
((_ () value)
value)
((_ (decorator next ...) value)
(decorator (wrap-decorators (next ...) value)))))
(define-syntax define
(syntax-rules (@)
((_ (@ decorator ...) (id . params) body ...)
(define (@ decorator ...) id (lambda params body ...)))
((_ (@ decorator ...) id value)
(define id (wrap-decorators (decorator ...) value)))
((_ other ...)
(basic-define other ...))))
(define (trace label)
(lambda (f)
(lambda args
(dynamic-wind (thunk (eprintf "enter ~a: ~s~%" label args))
(thunk (apply f args))
(thunk (eprintf "exit ~a: ~s~%" label args))))))
现在你可以这样使用它:
(define (@ (trace 'hypot)) (hypot x y)
(sqrt (+ (sqr x) (sqr y))))
这会导致hypot
函数被trace
包裹,因此当您调用它时,会发生跟踪:
> (hypot 3 4)
enter hypot: (3 4)
exit hypot: (3 4)
5
或者,使用uselpa的memoize
函数,您可以使用:
(define (@ memoize) (fib n)
(if (< n 2)
n
(+ (fib (sub1 n)) (fib (- n 2)))))
并获得快速的memoised fib
功能。您甚至可以跟踪和记忆它,仅显示实际(缓存未命中)调用:
(define (@ (trace 'fib) memoize) (fib n)
(if (< n 2)
n
(+ (fib (sub1 n)) (fib (- n 2)))))
请注意,在我的宏中,我将Racket的define
导入basic-define
,以便我重新定义的define
可以委托给它。
答案 1 :(得分:3)
如果您想将原始define
绑定到其他符号,您可以:
#lang racket
(require (rename-in racket (define mydef)))
(mydef n 2)
(mydef (times2 n) (* 2 n))
(times2 n)
=> 4
现在可以重新定义define
,但根据您的具体情况,您最终可能会将其定义为宏< / em>而不是一个函数。在define
宏(或函数)中,您仍然可以使用现在绑定到define
的原始mydef
。
在某些情况下重新定义define
可能有意义。 替代可以使用类似于Python 装饰器的东西。这是备忘录的一个例子。假设这个程序:
(define (memoize fn)
(let ((cache (make-hash)))
(λ arg (hash-ref! cache arg (thunk (apply fn arg))))))
和经典的Fibonacci程序:
(define fib
(lambda (n)
(if (< n 2) n (+ (fib (sub1 n)) (fib (- n 2))))))
(time (fib 35))
cpu time: 3039 real time: 3036 gc time: 0
9227465
可以改写
(define fib
(memoize
(lambda (n)
(if (< n 2) n (+ (fib (sub1 n)) (fib (- n 2)))))))
(time (fib 35))
cpu time: 1 real time: 0 gc time: 0
9227465
答案 2 :(得分:3)
其他两个答案已经提供了很好的解释,所以我只是添加一个更具特定于Racket的例子。
在Racket中,您可以构建自己的#lang
,其处理定义与基本的Racket语言不同。例如,Typed Racket的定义表单允许使用如下代码:
(define (fact [n : Integer]) : Integer
(if (zero? n)
1
(* n (fact (sub1 n)))))
此define
表单允许使用额外的类型注释与Typed Racket的类型检查器进行通信。如果无法覆盖#lang
中的核心表单,则无法无缝添加类型注释。