gpt4 book ai didi

macros - 由不是列表头部的关键字触发的方案宏

转载 作者:太空宇宙 更新时间:2023-11-03 18:35:23 25 4
gpt4 key购买 nike

假设我想在 s-expression 中的第一项以外的其他内容上触发 Scheme 宏。例如,假设我想用中缀样式 := 替换 define,这样:

(a := 5) -> (define a 5)
((square x) := (* x x)) -> (define (square x) (* x x))

实际的转换似乎非常简单。诀窍是让 Scheme 找到 := 表达式并宏展开它们。我考虑过使用标准宏围绕使用中缀语法的大部分代码,可能是:(with-infix-define expr1 expr2 ...),并让标准宏遍历在其主体中表达并执行任何必要的转换。我知道如果我采用这种方法,我将必须小心避免转换实际上应该是数据的列表,例如引用列表和准引用列表的某些部分。我设想的一个例子:

(with-infix-define
((make-adder n) := (lambda (m) (+ n m)))

((foo) :=
(add-3 := (make-adder 3))
(add-6 := (make-adder 6))
(let ((a 5) (b 6))
(+ (add-3 a) (add-6 b))))

(display (foo))
(display '(This := should not be transformed))

所以,我的问题有两个方面:

  1. 如果我采用 with-infix-define 路线,我是否需要注意除引用和准引用之外的任何障碍?
  2. 我觉得自己有点像在重新发明轮子。这种类型的代码遍历看起来完全标准宏扩展系统必须做的事情——唯一的区别是它们在决定是否进行任何代码转换时只查看列表中的第一项.有什么方法可以让我搭便车在现有系统上吗?

最佳答案

  1. 在您继续此操作之前,最好彻底考虑一下 -- IME 您经常会发现您真正想要的是将 := 作为中缀的读者级处理句法。这当然意味着它也中缀在引号等中,所以现在看起来很糟糕,但同样,我的经验是你最终会意识到最好始终如一地做事。

  2. 为了完整起见,我会提到在 Racket 中有一个针对类似中缀表达式的读取语法黑客:(x . define . 1)读取 作为 (定义 x 1)。 (和上面一样,它无处不在。)

  3. 否则,您的包装宏想法几乎是您唯一可以做的事情。但这并没有使它完全没有希望,你可能有一个钩子(Hook)到你的实现的扩展器中,它可以让你做这样的事情——例如,Racket 有一个特殊的宏,叫做 #%module-begin包装了一个完整的模块体和#%top-interaction在 REPL 上包装顶级表达式。 (这两个都在两个上下文中隐式添加。)这是一个示例(为简单起见,我使用 Racket 的 define-syntax-rule):

    #lang racket/base

    (provide (except-out (all-from-out racket/base)
    #%module-begin #%top-interaction)
    (rename-out [my-module-begin #%module-begin]
    [my-top-interaction #%top-interaction]))

    (define-syntax infix-def
    (syntax-rules (:= begin)
    [(_ (begin E ...)) (begin (infix-def E) ...)]
    [(_ (x := E ...)) (define x (infix-def E) ...)]
    [(_ E) E]))

    (define-syntax-rule (my-module-begin E ...)
    (#%module-begin (infix-def E) ...))
    (define-syntax-rule (my-top-interaction . E)
    (#%top-interaction . (infix-def E)))

    如果我把它放在一个名为 my-lang.rkt 的文件中,我现在可以按如下方式使用它:

    #lang s-exp "my-lang.rkt"
    (x := 10)
    ((fib n) :=
    (done? := (<= n 1))
    (if done? n (+ (fib (- n 1)) (fib (- n 2)))))
    (fib x)
  4. 是的,您需要处理很多事情。上面的两个示例是处理 begin 表达式和处理函数体。这显然是一个非常局部的列表——您还需要 lambdalet 等的主体。但这仍然比一些盲目按摩要好,因为那不是实用,因为您无法真正提前知道一些随机代码将如何结束。作为一个简单的例子,考虑这个简单的宏:

    (define-syntax-rule (track E)
    (begin (eprintf "Evaluating: ~s\n" 'E)
    E))
    (x := 1)
  5. 这样做的结果是,为了获得合适的解决方案,您需要一些方法来预扩展代码,以便您可以扫描代码并处理实现中的少数已知核心形式。

  6. 是的,所有这些都是宏扩展器所做的重复工作,但由于您正在改变扩展的工作方式,因此没有办法解决这个问题。 (要了解为什么这是一个根本性的变化,请考虑像 (if := 1) 这样的东西——这是一个条件表达式还是一个定义?你如何决定哪个优先?)出于这个原因,对于具有这种“可爱语法”的语言,更流行的做法是将代码读取并解析为普通的 S 表达式,然后让实际的语言实现使用普通的函数和宏。

关于macros - 由不是列表头部的关键字触发的方案宏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10791481/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com