是否有一个有效的用例来重新定义"定义#34;在计划/球拍?

时间:2014-04-19 13:33:16

标签: scheme racket language-features

我正在玩球拍/方案,它允许我重新定义实例define并将其绑定为值。

> (define define 2)
> define
2

在该范围内,我无法再使用define定义任何内容,因为它显然已绑定到2.这适用于我尝试过的所有“关键字”(ifcond等)。

但是,无法使用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?我意识到这有点基于意见,但我正在寻找这个可能可能是合理的事情的用例(无论是否是另一回事)。

3 个答案:

答案 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中的核心表单,则无法无缝添加类型注释。