我已经使用Guile大约一年了,但在Scheme中使用宏方面相当缺乏经验。尽管我有一些更复杂的例子可以令人满意地工作,但我一直被(对我来说(感觉像是一个非常简单的用例所困扰,类似于类似于在C.中使用#define
可以实现的简单替换
我有一个函数,它使用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
我天真地添加了它,就像下面一样——我在第二节中评论了";沿着x折叠="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
-子句中使用的测试和表达式?这是一件合理/明智的事情吗?
cond
将在宏之前展开。因此
(cond
...
(fold-parse line 'x)
...)
将首先变成:
(if ...
(if fold-parse
(begin line 'x)
...)
因此,你可能会得到一个未绑定的变量错误,或者猪飞了。无论如何,cond
的工作原理是,如果第二项只有一个测试,则真实值将是cond
的结果,因此您可以这样做:
(define (handle-fold line var)
(let ((str (string-append "fold along " (symbol->string var) "=")))
(and (string= line str 1 13 1 13)
(list var (last (string-split line #=))))))
在你的cond
:
(cond
((string-any (cut eqv? <> #,) line)
(string-split line #,))
((string-null? line) #f) ;; blank line
((handle-fold line 'x)) ;; NB: The truthy return is the result
((handle-fold line 'y)) ;; NB: The truthy return is the result
(else (error "bad input!"))))))))
现在看看代码的数量,它并没有变得容易多少,所以我会对最初的版本感到满意,如果类似的行数进一步增加,我可能会开始考虑替代方案。现在,在未来的版本中,这两条线可能会发生变化并有所不同,这项工作可能会付诸东流。这种情况发生的次数比我的预测对我未来的帮助要多,但我也喜欢保持干燥。