如何避免模式中的代码重复?

How to avoid code repetition in patterns?

如果我有像 (structure-type-name field1-pattern (app some-function pattern-with-variables-a-b) (app some-function pattern-with-variables-c-d)) 这样的模式,我可以使用什么来代替为后两个字段重复大量代码以仍然具有 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))))])

您可以使用 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))])

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

无论如何,我并不是要提供您可以随时使用的代码。我只是给你举个例子,这样你就可以根据自己的需要进行调整。