如何将标识符绑定到子列表加分隔符?

How to bind an identifier to a sublist plus delimiter?

提问人:formalizm 提问时间:2/21/2023 更新时间:2/22/2023 访问量:73

问:

这会将标识符绑定到列表的前缀和后缀:matchab'(0 1)'(3 4 5)

(match '(0 1 2 3 4 5)
  [`(,a ... 2 ,b ...)
   (values a b)])

另一个等效版本:

(match '(0 1 2 3 4 5)
  [`(,@(list a ... 2) ,b ...)
   (values a b)])

如何将标识符(在模式本身内)绑定到前缀,包括分隔符?'(0 1 2)

模式匹配 匹配 球拍 子列表

评论


答:

1赞 Shawn 2/22/2023 #1

该模式调用一个具有匹配值的函数,然后匹配它返回的值,并结合在第一个列表而不是第二个列表中包含分区元素的 splitf-at 版本,可用于执行此操作:app

; Like splitf-at but includes the element to split at in the first value not the second
(define (splitf-at/inclusive lst pred?)
  (let loop ([lst lst]
             [first-res '()])
    (cond
      ((empty? lst)
       (values (reverse first-res) '()))
      ((pred? (car lst))
       (loop (cdr lst) (cons (car lst) first-res)))
      (else
       (values (reverse (cons (car lst) first-res)) (cdr lst))))))

; Gives '(0 1 2) '(3 4 5)
(match '(0 1 2 3 4 5)
  ((? list? (app (lambda (lst) (splitf-at/inclusive lst (negate (curry = 2)))) a b))
   (values a b)))

(请注意,在尝试调用依赖于该值的任何函数之前,请使用 来确保该值是一个列表。(? list? ...)

您可以定义匹配扩展器以使其看起来更漂亮:

(define-match-expander split-list
  (lambda (stx)
    (syntax-case stx (...)
      ((split-list x (... ...) val y (... ...))
       #'(? (lambda (lst) (and (list? lst) (member val lst)))
            (app (lambda (lst) (splitf-at/inclusive lst (lambda (elem) (not (equal? elem val))))) x y))))))

; Also gives '(0 1 2) '(3 4 5)
(match '(0 1 2 3 4 5)
  ((split-list a ... 2 b ...)
   (values a b)))

此版本还包括一项检查,以确保要拆分的值实际上在列表中,否则将无法匹配。

评论

0赞 formalizm 2/25/2023
谢谢@Shawn,这是一个很好的方法,我会使用它。
0赞 formalizm 2/25/2023
不过,对缺点的一些评论:1)第一个“......”在这个匹配扩展器中并不贪婪,不像通常的 match 省略号(这当然很容易修复),2) 与匹配中的列表模式相比,这种语法非常严格。例如,最好允许任意数量的标识符和嵌套列表子模式,并在其中捕获一些标识符。这超出了我的问题范围,但需要跟进(使用匹配扩展器转换为列表子句),我会考虑一下。