Racket - 使用宏实现let *功能

时间:2017-05-15 17:58:57

标签: macros scheme lisp racket let

我需要使用 defmacro 来实现my_let *,它与let *类似,但是当let *扩展为一系列嵌套let调用(幕后)时,my_let *需要扩展进行单次调用调用,并使用 define 语句定义我得到的参数。

使用my_let *:

的示例
 (my_let* ((a 2)
 (b 3)
 (c (+ a b)))
 (+ a b c))

并且此代码的​​返回值应为10.就像使用let *一样。 上面的代码将在 my_let * 中扩展为以下代码:

(let ()
 (define a 2)
 (define b 3)
 (define c (+ a b))
 (+ a b c))

我是使用宏的新手,虽然我成功地写了一些宏,但这让我迷失了 提前谢谢。

3 个答案:

答案 0 :(得分:3)

使用syntax-parse。至少不要考虑在Racket中使用defmacro

#lang racket

(require (for-syntax syntax/parse))

(define-syntax (my-let* stx)
  (syntax-parse stx
    [(_my-let* ([name:id e:expr] ...) body ...)
     #'(let ()
         (define name e) ...
         body ...)]))

name:id表示name必须是标识符,e:expr表示 那个e必须是一个表达式。这些简单的注释有助于语法分析 给你更好的错误信息。

示例:

(my-let* ((4 2)
          (b 3)
          (c (+ a b)))
         (+ a b c))

DrRacket将为4读取颜色并给出消息:

my-let*: expected identifier in: 4

答案 1 :(得分:0)

Scheme方式正在使用syntax-rules

(define-syntax my-let*
  (syntax-rules ()
    ((_ ((binding expression) ...) body ...)
     (let ()
       (define binding expression) ...
       body ...))))

使用defmacro更像是制作程序。

(define (my-let-fun* bindings . body)
  ...)

它应该如何工作是这样的:

(my-let-fun* '((a 1) (b 2) (c (+ a b))) "test" '(list a b c))
; ==> (let () (define a 1) (define b 2) (define c (+ a b)) "test" (list a b c))

如果您未在实施中调用my-let-fun*,则只需将其更改为defmacro即可。

(defmacro my-let* (bindings . body)
  ...)

使用帮助程序进行递归或foldr进行绑定非常简单。祝你好运!

您的my-let*仅适用于#lang racket,也许#!r6rs及更高版本。在R5RS中,在这种情况下您将收到错误:

(my-let* ((a 1) (b 2) (c (+ a b)))
  (list a b c))
; signals an error that a is undefined.

原因是它扩展到这样的东西:

(let ((a 'undefined) (b 'undefined) (c 'undefined))
  (let ((tmp1 1) (tmp2 2) (tmp3 (+ a b)))
    (set! a tmp1)
    (set! b tmp2)
    (set! c tmp3))
  (list a b c))

答案 2 :(得分:0)

在错误消息和宏步进器的一些明智使用之间,我认为这里很难出错。麻烦只是确保你使用conses或unquote-sclicing把事情放在一起。我相信这些宏的标准做法是大量使用quasiquote和unquote-splicing,以使输出尽可能与预期的语句紧密匹配,否则宏可能变得非常难以理解。但我不是defmacro专家。

#lang racket/base
(require (for-syntax racket/base)
         compatibility/defmacro)

(defmacro my-let* (binding-pairs . body)
  (define defines (map (lambda (bp) (cons 'define bp)) binding-pairs))
  `(let ()
     ,@defines
     ,@body))

(my-let* ((a 2) 
          (b (expt a 3)))
  (printf "a:~a\nb:~a\n" a b)
  (+ a b))