我已经使用 Guile 大约一年了,但在 Scheme 中使用宏的经验相当缺乏。尽管我有一些更复杂的示例可以令人满意地工作,但我仍然坚持(对我来说)感觉像是一个非常简单的用例,类似于类似于#define
C中可以实现的简单替换。
我有一个cond
用于测试多个条件的函数,其中一些具有通用形式。例如:
(define (file->list filename)
"Read input and split into list of
coordinates and folds."
(let ((lst (call-with-input-file filename
(λ (p)
(list-ec (:port line p read-line)
(cond
((string-any (cut eqv? <> #\,) line)
(string-split line #\,))
((string-null? line) #f) ;; blank line
((string= line "fold along x=" 1 13 1 13)
`(x ,(last (string-split line #\=))))
((string= line "fold along y=" 1 13 1 13)
`(y ,(last (string-split line #\=))))
(else (error "bad input!"))))))))
(parse-input lst)))
我想摆脱围绕以下表格条件的重复:
((string= line "fold along x=" 1 13 1 13)
`(x ,(last (string-split line #\=))))
这对我来说就像一个宏,因为这个样板可以在编译时使用模式匹配生成——我天真地尝试过这样的事情:
(define-syntax-rule (fold-parse str x-or-y)
`((string= ,str
,(string-append "fold along " (symbol->string x-or-y) "=")
1 13 1 13)
(x-or-y ,(string->number (last (string-split str #\=))))))
这确实在 REPL 中重现了(测试表达式)s 表达式:
scheme@(guile-user)> (fold-parse "fold along x=3" 'x)
$33 = ((string= "fold along x=3" "fold along x=" 1 13 1 13) ((quote x) 3))
scheme@(guile-user)>
但是当我尝试将宏插入我的时,cond
我收到以下错误:
;;; WARNING: compilation of /home/foo/dev/aoc_2021/13/./13.scm failed:
;;; Syntax error:
;;; /home/foo/dev/aoc_2021/13/./13.scm:53:28: source expression failed to match any pattern in form fold-parse
ice-9/psyntax.scm:2794:12: In procedure syntax-violation:
Syntax error:
unknown location: source expression failed to match any pattern in form fold-parse
我很天真地添加它,就像下面一样 - 我已经注释掉了 cond 中的样板,它围绕着“fold along x =”样板,它意味着要替换:
(define (file->list filename)
"Read input and split into list of
coordinates and folds."
(let ((lst (call-with-input-file filename
(λ (p)
(list-ec (:port line p read-line)
(cond
((string-any (cut eqv? <> #\,) line)
(string-split line #\,))
((string-null? line) #f) ;; blank line
(fold-parse line 'x)
;;((string= line "fold along x=" 1 13 1 13)
;; `(x ,(last (string-split line #\=))))
((string= line "fold along y=" 1 13 1 13)
`(y ,(last (string-split line #\=))))
(else (error "bad input!"))))))))
(parse-input lst)))
自从这次尝试以来,我一直在, 和许多其他宏的变体中陷入困境,并syntax-case
尝试使其工作。quasisyntax
cond
但是,我显然没有得到关于宏可以“插入替换”片段或表达式的一部分来代替它们的方式的根本重要的东西。
谁能帮我看看我的方式的错误?
如何编写一个可以生成要在 - 子句中使用的测试和表达式的宏cond
?而且 - 这是一件合理/明智的事情吗?