scheme - 方案 - 引用和辅助语法
问题描述
由于某种原因,以下宏将无法与引号一起使用。
(define-syntax expand
(lambda (stx)
(syntax-case stx (break)
[(_ k () (ys ...)) (begin (println (syntax->datum #'(ys ...))) #'(ys ...))]
[(_ k ((break) xs ...) (ys ...)) #'(expand k (xs ...) (ys ... (k (void))))]
[(_ k ((es ...) xs ...) (ys ...)) #'(expand k (xs ...) (ys ... (expand k (es ...) ())))]
[(_ k (a xs ...) (ys ...)) #'(expand k (xs ...) (ys ... a))])))
(define-syntax loop
(syntax-rules ()
[(_ e es ...)
(call/cc (lambda (k)
(let l ()
(expand k (begin e es ...) ())
(l))))]))
(loop (list 1 (break)))
;; => works fine
(loop (quasiquote (1 (unquote (break)))))
;; => break: unbound identifier in module in: break
看到为什么第二个案例失败了,我有点惊讶。
并且为这两种情况打印以下调试信息。
;; case 1
'(begin (expand k (list 1 (break)) ()))
'(list 1 (k (void)))
;; case 2
'(begin (expand k `(1 ,(break)) ()))
'`(expand k (1 ,(break)) ())
请注意,在quasiquote
扩展后案例 2 的输出中,其余部分(1 ,(break))
未扩展。
不知道为什么会发生这种情况。
谢谢
解决方案
问题是宏扩展器不会扩展出现在 aquote
或下的宏调用quasiquote
。例如:
(define-syntax-rule (pipe) "|")
> (quote (pipe))
'(pipe) ; not "|"
> (quasiquote (pipe))
'(pipe) ; not "|"
这可以通过在编译时直接对语法对象进行递归来解决,而不是通过返回内部带有宏调用的语法对象来进行递归。
一般来说,翻译这样的代码:
(define-syntax expand
(lambda (stx)
(syntax-case stx literals
cases
[pattern #'(.... (expand stuff) ...)]
cases)))
变成这样的代码:
(begin-for-syntax
(define (expand stx)
(syntax-case stx literals
cases
[pattern #`(.... #,(expand stuff) ...)]
cases)))
在您的特定情况下,您可能希望expand
成为一个 3 参数函数,它在编译时完全运行和重复。
(begin-for-syntax
(define (expand k xs ys)
(with-syntax ([(ys ...) ys])
(syntax-case xs (break)
[() (begin (println (syntax->datum #'(ys ...))) #'(ys ...))]
[((break) xs ...) (expand k #'(xs ...) #'(ys ... (k (void))))]
[((es ...) xs ...) (expand k #'(xs ...) #`(ys ... #,(expand k #'(es ...) #'())))]
[(a xs ...) (expand k #'(xs ...) #'(ys ... a))]))))
loop
然后你可以在宏的实现中调用这个编译时函数:
(define-syntax loop
(lambda (stx)
(syntax-case stx ()
[(_ e es ...)
#`(call/cc (lambda (k)
(let l ()
#,(expand #'k #'(begin e es ...) #'())
(l))))])))
但是,这不是执行循环宏的最佳方式。
我希望上面的编译时功能可以帮助您了解宏的可能性。但是,对于loop
宏来说,它不应该是必需的。语法参数提供了一种更简单的方法来执行此操作。
(define-syntax-parameter break
(lambda (stx) (raise-syntax-error #f "cannot be used outside of loop" stx)))
(define-syntax loop
(syntax-rules ()
[(_ e es ...)
(call/cc (lambda (k)
(define (break-function) (k (void)))
(syntax-parameterize ([break (make-rename-transformer #'break-function)])
(let l ()
(begin e es ...)
(l)))))]))
事实上,loop
像这样的宏是论文使用语法参数保持清洁第 4 节中使用的示例之一,称为forever
,它调用破坏语法参数abort
。
推荐阅读
- javascript - 在 javascript 中检测 SFSafariViewController
- reactjs - “react-scripts start”之后的顺序 NPM 脚本
- r - 使用非计数统计在条形图上叠加频率多边形
- amazon-web-services - 当我尝试从邮递员访问 api 端点时出现内部服务器错误
- android - 在 Android 中获取 mp4 文件的位深度或颜色空间
- python-3.x - 识别大数据字符串中的数字,这些数字以字母表为前缀,在其他字符之间最多 2 个位置
- java - Hibernate:如何使用多态映射 @ManyToOne (@ManyToAny)
- javascript - 光滑滑块中的 slickGoTo 方法不起作用
- c# - 锁定方法直到它完成执行
- macos - macOS 上 AVAudioSession inputLatency 和 outputLatency 属性的等价物