在方案中生成 cond(测试表达式...)



我已经使用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-casequasisyntax以及宏和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!"))))))))

现在看看代码的数量,它并没有变得容易多少,所以我会对最初的版本感到满意,如果类似的行数进一步增加,我可能会开始考虑替代方案。现在,在未来的版本中,这两条线可能会发生变化并有所不同,这项工作可能会付诸东流。这种情况发生的次数比我的预测对我未来的帮助要多,但我也喜欢保持干燥。

最新更新