如何使用球拍宏将对f
的所有函数调用替换为对g
的函数调用?我是球拍的新手,我不知道如何处理语法对象,但是我相信我想到的用例是球拍宏可以做的事情。考虑以下示例,其中我想用plus
替换mul
。宏replace-plus-with-mul
仅返回current-seconds
作为占位符,因为我不知道如何用plus
替换mul
的语法对象。宏可以这样做吗?
#lang racket
(define-syntax replace-plus-with-mul
(lambda (stx) #'(current-seconds)))
(define plus (lambda (x y) (+ x y)))
(define mul (lambda (x y) (* x y)))
(define a 4)
(define b 2)
(define c (plus a b))
(replace-plus-with-mul d c) ;; (define d (mul a b))
(print d) ;; should print 8
答案 0 :(得分:4)
我没有找到一种简单的方法来精确地获得您想要的工作,但是有一个额外的限制,这肯定是可能的。
如果您对宏调用必须在语法上包含plus
的限制感到满意,那么只需用宏内的plus
递归替换所有mul
;; main.rkt
#lang racket
(define plus (lambda (x y) (+ x y)))
(define mul (lambda (x y) (* x y)))
(define-for-syntax (replace stx)
(syntax-case stx ()
[(a . b)
(datum->syntax stx (cons (replace #'a)
(replace #'b)))]
[_
(and (identifier? stx)
(free-identifier=? #'plus stx))
#'mul]
;; FIXME: need more cases (like box or vector), but
;; this is sufficient for the demo
[_ stx]))
(define-syntax (replace-plus-with-mul stx)
(syntax-case stx ()
[(_ id expr)
#`(define id
#,(replace (local-expand #'expr 'expression '())))]))
(replace-plus-with-mul c (plus 3 (let ([plus 10]) plus)))
c ; prints 30
(plus 3 (let ([plus 10]) plus)) ; prints 13
如果您对要更改的plus
的限制一定没有被使用感到满意,例如以下代码:
(define (c) (plus 3 2))
(replace-plus-with-mul d (c))
然后有几种解决方法。一种是覆盖#%module-begin
,将所有plus
替换为(if (current-should-use-mul?) mul plus)
,然后将replace-plus-with-mul
扩展为(parameterize ([current-should-use-mul? #t]) ...)
。这是完整的代码:
;; raquet.rkt
#lang racket
(provide (except-out (all-from-out racket)
#%module-begin)
(rename-out [@module-begin #%module-begin])
plus
mul
replace-plus-with-mul)
(define plus (lambda (x y) (+ x y)))
(define mul (lambda (x y) (* x y)))
(define current-should-use-mul? (make-parameter #f))
(define-for-syntax (replace stx)
(syntax-case stx ()
[(a . b)
(datum->syntax stx (cons (replace #'a)
(replace #'b)))]
[_
(and (identifier? stx)
(free-identifier=? #'plus stx))
#'(if (current-should-use-mul?) mul plus)]
;; FIXME: need more cases (like box or vector), but
;; this is sufficient for the demo
[_ stx]))
(define-syntax (@module-begin stx)
(syntax-case stx ()
[(_ form ...)
#'(#%module-begin (wrap-form form) ...)]))
(define-syntax (wrap-form stx)
(syntax-case stx ()
[(_ form) (replace (local-expand #'form 'top-level '()))]))
(define (activate f)
(parameterize ([current-should-use-mul? #t])
(f)))
(define-syntax (replace-plus-with-mul stx)
(syntax-case stx ()
[(_ id expr)
#`(define id (activate (lambda () expr)))]))
和
;; main.rkt
#lang s-exp "raquet.rkt"
(define (c) (plus 3 (let ([plus 10]) plus)))
(replace-plus-with-mul a (c))
a ; prints 30
(c) ; prints 13
从某种意义上说,您想要做的事情需要某种懒惰的评估,这是一个巨大的语义变化。我不确定在没有“破坏”其他代码的情况下是否有一个很好的方法。
答案 1 :(得分:2)
您可以通过定义自己的define
版本来实现此目的,该版本可以保存编译时的表达式,replace-plus-with-mul
以后可以获取。
两个宏define/replacable
和replace-plus-with-mul
必须使用define-syntax
和syntax-local-value
一起工作:
define/replacable
使用define-syntax
将编译时信息与其定义的标识符相关联。replace-plus-with-mul
使用syntax-local-value
查找该编译时信息。define-syntax
中保存函数#lang racket
(require syntax/parse/define
(for-syntax syntax/transformer))
(define-syntax-parser define/replacable
[(_ name:id expr:expr)
#:with plus (datum->syntax #'name 'plus)
#:with mul (datum->syntax #'name 'mul)
#'(define-syntax name
;; Identifier Identifier -> Expression
;; Replaces plus and mul within the expr
;; with the two new identifiers passed to
;; the function
(lambda (plus mul)
(with-syntax ([plus plus] [mul mul])
#'expr)))])
(define-syntax-parser replace-plus-with-mul
[(_ name:id replacable:id)
(define replace (syntax-local-value #'replacable))
#`(define name #,(replace #'mul #'mul))])
使用这些定义,该程序可以工作:
(define plus (lambda (x y) (+ x y)))
(define mul (lambda (x y) (* x y)))
(define a 4)
(define b 2)
(define/replacable c (plus a b))
(replace-plus-with-mul d c) ;; (define d (mul a b))
(print d)
;=output> 8
但是,本示例中的c
不能用作正则表达式。它可以在replace-plus-with-mul
中使用,但只能在其中使用。可以通过添加结构来解决此问题。
在第一个版本中,两个宏的通信方式如下:
define/replacable
使用define-syntax
将编译时信息与其定义的标识符相关联。replace-plus-with-mul
使用syntax-local-value
查找该编译时信息。但是,这不允许标识符具有正常行为。为此,我们需要这样的东西:
define/replacable
使用define-syntax
将其定义的标识符与包含两个的编译时结构相关联:
replace-plus-with-mul
使用syntax-local-value
查找该编译时结构,并从中获取replace
行为syntax-local-value
查找该编译时结构,并将其用作过程以用作宏。因此,我们应该使该结构具有正常行为的#:property prop:procedure
。此结构可以如下所示:
(begin-for-syntax
;; normal : Expression -> Expression
;; replace : Identifier Identifier -> Expression
(struct replacable-id [normal replace]
#:property prop:procedure (struct-field-index normal)))
现在define/replacable
宏应生成一个define-syntax
来构造其中之一:
(define-syntax name
(replacable-id ???
(lambda (plus mul)
...what-we-had-before...)))
如果我们希望正常行为看起来像一个变量,则可以使用???
中的make-variable-like-transformer
来填充syntax/transformer
孔:
(require (for-syntax syntax/transformer))
(begin-for-syntax
;; Identifier -> [Expression -> Expression]
(define (make-var-like-transformer id)
(set!-transformer-procedure (make-variable-like-transformer id))))
然后define/replacable
可以生成如下内容:
(define normal-name expr)
(define-syntax name
(replacable-id (make-var-like-transformer #'normal-name)
(lambda (plus mul)
...what-we-had-before...)))
将它们放在一起:
#lang racket
(require syntax/parse/define
(for-syntax syntax/transformer))
(begin-for-syntax
;; Identifier -> [Expression -> Expression]
(define (make-var-like-transformer id)
(set!-transformer-procedure (make-variable-like-transformer id)))
;; normal : Expression -> Expression
;; replace : Identifier Identifier -> Expression
(struct replacable-id [normal replace]
#:property prop:procedure (struct-field-index normal)))
(define-syntax-parser define/replacable
[(_ name:id expr:expr)
#:with plus (datum->syntax #'name 'plus)
#:with mul (datum->syntax #'name 'mul)
#'(begin
(define normal-name expr)
(define-syntax name
(replacable-id (make-var-like-transformer #'normal-name)
(lambda (plus mul)
(with-syntax ([plus plus] [mul mul])
#'expr)))))])
(define-syntax-parser replace-plus-with-mul
[(_ name:id replacable:id)
(define value (syntax-local-value #'replacable))
(define replace (replacable-id-replace value))
#`(define name #,(replace #'mul #'mul))])
并尝试一下:
(define plus (lambda (x y) (+ x y)))
(define mul (lambda (x y) (* x y)))
(define/replacable a 4)
(define/replacable b 2)
(define/replacable c (plus a b))
(replace-plus-with-mul d c) ;; (define d (mul a b))
(print d)
;=output> 8