我已经使用了大约一年的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中再现了(test表达式)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=折叠”样板周围的样板注释掉了,它的意思是要替换:
(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-clause中使用的宏?那么-这是一种合理/明智的做法吗?
发布于 2021-12-31 19:18:30
cond将在宏之前展开。因此。
(cond
...
(fold-parse line 'x)
...)将首先转化为:
(if ...
(if fold-parse
(begin line 'x)
...)因此,您可能会得到一个未绑定的变量错误,或者可能是猪飞。无论如何,cond的工作原理是,如果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!"))))))))现在,看看代码的数量,它并没有变得简单得多,所以我会对最初的版本感到满意,并且可能开始考虑如果类似的行数进一步增加的话。现在,这两行可能会改变,在未来的版本中会有所不同,并且可能会失去努力。它的发生比我的预测更多地帮助我在未来,但我也喜欢保持干燥。
https://stackoverflow.com/questions/70544527
复制相似问题