【问题标题】:How to avoid code repetition in patterns?如何避免模式中的代码重复?
【发布时间】:2021-11-07 18:50:17
【问题描述】:

如果我有像(structure-type-name field1-pattern (app some-function pattern-with-variables-a-b) (app some-function pattern-with-variables-c-d)) 这样的模式,我可以使用什么来代替为后面的 2 个字段重复大量代码以仍然有 abcd 绑定?或者,我也会对分别包含 `(,a ,c)`(,b ,d) 列表的 2 个绑定变量感到满意。

编辑

#lang racket

(struct my-struct (field1 field2 field3) #:transparent)

(define s (my-struct '(3 4) '(6 5) '(7 8)))

(match s [(my-struct `(,x ,y)
                    (app (curryr sort <) `(,lo1 ,hi1))
                    (app (curryr sort <) `(,lo2 ,hi2)))
          (- (* x lo1 lo2) (* y hi1 hi2) (expt lo1 hi2))])

(match s [(app struct->vector
               (vector 'struct:my-struct p (app (curryr sort <) `(,lo ,hi)) ...))
; The following line is long and the computation artificial, but just for the sake of an example.
; However, imagine my-struct having more fields.
          (apply - `(,@(map (curry apply *) (map cons p `(,lo ,hi))) ,(expt (car lo) (cadr hi))))])

(match s [(app (compose vector->list struct->vector)
               (list 'struct:my-struct p (app (curryr sort <) `(,lo ,hi)) ...))
          (apply - `(,@(map (curry apply *) (map cons p `(,lo ,hi))) ,(expt (car lo) (cadr hi))))])

(require racket/struct) ; provides struct->list

(match s [(? my-struct?
             (app struct->list
                  (list p (app (curryr sort <) `(,lo ,hi)) ...)))
          (apply - `(,@(map (curry apply *) (map cons p `(,lo ,hi))) ,(expt (car lo) (cadr hi))))])

【问题讨论】:

  • 我想出了一个解决方案,使用额外的 appstruct-&gt;vector(或 struct-&gt;list,尽管奇怪的是 DrRacket 并没有默认提供它,尽管它位于球拍参考)然后(vector 'struct:structure-type-name field1-pattern (app some-function pattern-with-variables-a-b) ...)。但这很冗长。我想还有更好的方法。
  • 也许有准报价?还是一个宏(最好在本地定义,类似于lambda 的匿名函数,但在语法上)?
  • 你能提供一个具体的例子吗?你想让它看起来像什么?目前的代码是什么样的?

标签: macros pattern-matching racket


【解决方案1】:

你可以使用define-match-expander,像这样:

#lang racket

(require (for-syntax syntax/parse))

(define-match-expander sorted-list
  (syntax-parser
    [(_ x ...) #'(app (curryr sort <) (list x ...))]))

(struct my-struct (field1 field2 field3) #:transparent)

(define s (my-struct '(3 4) '(6 5) '(7 8)))

(match s
  [(my-struct (list x y) (sorted-list lo1 hi1) (sorted-list lo2 hi2))
   (- (* x lo1 lo2) (* y hi1 hi2) (expt lo1 hi2))])

如果您的match 有多个分支,并且您想跳过sorted-list 与列表不匹配的分支,您还需要添加一个守卫:

#lang racket

(require (for-syntax syntax/parse))

(define-match-expander sorted-list
  (syntax-parser
    [(_ x ...)
     #'(? list? 
          (app (curryr sort <) (list x ...)))]))

(struct my-struct (field1 field2 field3) #:transparent)

(define s (my-struct '(3 4) 1 2))

(match s
  [(my-struct (list x y) (sorted-list lo1 hi1) (sorted-list lo2 hi2))
   (- (* x lo1 lo2) (* y hi1 hi2) (expt lo1 hi2))]
  [(my-struct (list x y) a b)
   (list x y a b)])

已编辑:这是另一个示例,说明如何进一步减少重复模式,但代价是不那么普遍。在这里,我假设第一个字段之后的每个字段都需要“排序列表”处理。

#lang racket

(require (for-syntax syntax/parse))

(struct my-struct (field1 field2 field3) #:transparent)

(define-match-expander my-struct*
  (syntax-parser
    [(_ x y (a ...) ...)
     #'(my-struct
        (list x y)
        (app (curryr sort <) (list a ...)) ...)]))

(define s (my-struct '(3 4) '(6 5) '(7 8)))

(match s
  [(my-struct* x y (lo1 hi1) (lo2 hi2))
   (- (* x lo1 lo2) (* y hi1 hi2) (expt lo1 hi2))])

我的问题是我不知道你想要的抽象级别是什么,我不知道你的结构的形状。例如,列表是否总是长度为 2?如果是这样,那么有一个看起来像(my-struct* x y lo1 hi1 lo2 hi2) 的模式甚至可能是有意义的。但如果没有,那就行不通了。

无论如何,我并不是要提供您可以轻松使用的代码。我只是给你一些例子,以便你可以根据自己的需要调整它们。

【讨论】:

  • 有趣,谢谢!它表明重复的部分可以简化为sorted-list 的单个调用。我宁愿接受一个答案,显示如何完全消除重复,并且不引入太多冗长,如问题中特别要求的那样,但如果没有这样的问题很快出现,它将是你的。
  • 我在上面又举了一个例子。
  • 太棒了!尽管我最终可能会使用其他东西,但您编辑的答案确实完全满足了所有规定的要求,并很好地了解了匹配扩展器的可能性,而像这样的用例似乎已经足够了。
猜你喜欢
  • 2011-08-29
  • 1970-01-01
  • 1970-01-01
  • 2020-08-31
  • 2012-06-19
  • 1970-01-01
  • 1970-01-01
  • 2018-08-16
相关资源
最近更新 更多