如何避免模式中的代码重复?
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))
这样的模式,我可以使用什么来代替为后两个字段重复大量代码以仍然具有 a
、b
、c
和d
绑定?或者,我也对分别包含列表 `(,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)
的模式甚至可能是有意义的。但如果不是,那就行不通了。
无论如何,我并不是要提供您可以随时使用的代码。我只是给你举个例子,这样你就可以根据自己的需要进行调整。
如果我有像 (structure-type-name field1-pattern (app some-function pattern-with-variables-a-b) (app some-function pattern-with-variables-c-d))
这样的模式,我可以使用什么来代替为后两个字段重复大量代码以仍然具有 a
、b
、c
和d
绑定?或者,我也对分别包含列表 `(,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)
的模式甚至可能是有意义的。但如果不是,那就行不通了。
无论如何,我并不是要提供您可以随时使用的代码。我只是给你举个例子,这样你就可以根据自己的需要进行调整。