macros - 如何让宏评估它的一些参数?
问题描述
我正在尝试制作一个使用宏 ( doc
) 编写文档的玩具系统:
示例 #1:
(doc id: 1
title: "First document"
"First sentence."
"Second sentence.")
预期扩展:
(make-doc (list (list 'id: 1) (list 'title: "First document"))
(list "First sentence" "Second sentence"))
示例 #2:
(let ((my-name "XYZ"))
(doc title: "Second document"
id: (+ 1 1)
"First sentence."
(string-append "My name is " my-name ".")
"Last sentence."))
预期扩展:
(let ((my-name "XYZ"))
(make-doc (list (list 'title: "Second document") (list 'id: (+ 1 1)))
(list "First sentence."
(string-append "My name is " my-name ".")
"Last sentence.")))
对该宏的更多示例调用是:
(doc id: 1 "First sentence." "Second sentence.")
(doc id: 1 title: "First document" subtitle: "First subdocument"
"First sentence." "Second sentence." "Third sentence.")
首先是元数据规范,然后是句子。元数据必须在句子之前。宏必须接受任意数量的元数据规范。
评估(doc ...)
应该返回一个字符串,或者将结果文本写入文件。但是我还没有实现这个功能,因为我被困在doc
宏的定义上(这是这个问题的重点)。
下面是我对doc
宏的实现。词汇:title: "ABC"
并被id: 123
称为“元数据”;title:
并被id:
称为“元数据 ID”。
;;; (metadata-id? 'x:) -> #t
;;; (metadata-id? 'x) -> #f
;;; (metadata-id? "Hi!") -> #f
(define (metadata-id? x)
(cond [(symbol? x)
(let* ([str (symbol->string x)]
[last-char (string-ref str (- (string-length str) 1))])
(char=? last-char #\:))]
[else #f]))
;;; (pair-elements '(1 2 3 4 5)) -> '((1 2) (3 4) (5)).
(define (pair-elements l [acc '()] [temp null])
(cond [(and (null? l) (null? temp)) acc]
[(null? l)
(append acc (list (list temp)))]
[(null? temp)
(pair-elements (cdr l) acc (car l))]
[else
(pair-elements (cdr l)
(append acc (list (list temp (car l)))))]))
(define-syntax doc
(syntax-rules ()
((doc arg . args)
(let* ([orig-args (cons 'arg 'args)]
[metadata-bindings (takef (pair-elements orig-args)
(lambda (e)
(metadata-id? (car e))))]
[sentences (drop orig-args (* 2 (length metadata-bindings)))])
(make-doc metadata-bindings sentences)))))
(define (make-doc metadata-bindings sentences)
;; Do something ...
;; Placeholder stubs:
(writeln metadata-bindings)
(writeln sentences))
使用此实现,评估示例 #1 会按预期打印:
((id: 1) (title: "First document"))
("First sentence." "Second sentence.")
但是,评估示例 #2 打印:
((id: (+ 1 1)) (title: "Second document"))
("First sentence." (string-append "My name is " my-name ".") "Last sentence.")
显然,这些论点没有被评估。示例 #2 的预期结果应该是这样的:
((id: 2) (title: "Second document"))
("First sentence." "My name is XYZ." "Last sentence.")
doc
宏的实现有什么问题?我怎样才能让宏评估它的一些参数?
解决方案
原因是您正在引用'args
,这导致它在宏扩展后成为 s 表达式,而不是使用函数应用程序进行评估。要解决此问题,您可能需要使用 quasiquote。这还需要您重新设计宏模式的指定方式。我建议使用...
符号。这是我所描述的草图:
(define-syntax doc
(syntax-rules ()
[(doc arg rest-args ...)
(let* ([orig-args `(arg ,rest-args ...)]
; the rest is the same
))]))
推荐阅读
- python - 如何使用python将字母转换为数字?
- javascript - 获取基本授权
- jquery - Jquery点击更改显示
- java - SwipeRefreshLayout 不遵守约束
- python - mongo_client TypeError:“模块”对象不可调用
- javascript - 在角度 6 我尝试使用 id 属性,但在绑定时它不适用于强标签和 innerhtml
- c# - GeoCoordinateWatcher.TryStart 在 C# 中无法正常工作
- vba - VBA 对象实例如何判断它是否是默认实例?
- c# - 在数字变为字母的地方拆分字符串,反之亦然
- git - git head 与 master 分离