Match: non-greedy, and also repeated elements?

28 views
Skip to first unread message

David Storrs

unread,
Dec 30, 2020, 2:24:55 PM12/30/20
to Racket Users
First off, is there a way to make ... in a pattern match non-greedily?  i.e., match as *few* elements as possible instead of as many?

Second, is there a way to make one pattern refer to an earlier pattern in the same match clause?  Semi-regularly I find myself wanting to do something like 'match repeated elements' or 'match if items from these two lists match'.  For example:

(match (list '(a b c) '(d e c))
  [(list (list _ ... x) (list _ ... y))
   #:when (equal? x y)
   'ok]
  [else 'nope])
=> 'ok

(match '(a b c c d)
  [(list _ ... x y _ ...)
   #:when (equal? x y)
   'ok]
  [else 'nope])
=> 'ok

Is there a way to do this without needing a #:when clause?

Jon Zeppieri

unread,
Dec 31, 2020, 2:06:32 PM12/31/20
to David Storrs, Racket Users
On Wed, Dec 30, 2020 at 2:24 PM David Storrs <david....@gmail.com> wrote:
>
> First off, is there a way to make ... in a pattern match non-greedily? i.e., match as *few* elements as possible instead of as many?

As far as I know, no. However, if your first example is really
illustrative of what you're trying to do, you could just use a `cons`
pattern instead of a `list` pattern:

(match (list '(a b c) '(d e c))
[(list (cons _ xs) (cons _ ys))
#:when (equal? xs ys)
'ok]
[else 'nope])
=> 'nope

And that makes a nice segue into your second question:

> Second, is there a way to make one pattern refer to an earlier pattern in the same match clause?

If we're limiting this to variable patterns, then yes. The
documentation says: "If an id is used multiple times within a pattern,
the corresponding matches must be the same according to
(match-equality-test), except that instances of an id in different or
and not sub-patterns are independent." So, assuming that
`(match-equality-test)` is `equal?` -- which is the default -- the
above could also be written as:

(match (list '(a b c) '(d e c))
[(list (cons _ xs) (cons _ xs))
'ok]
[else 'nope])

Similarly, your second example could be written as:

(match '(a b c c d)
[(list _ ... x x _ ...)
'ok]
[else 'nope])


However, I have seen some issues with this feature (using the same var
pattern multiple times to get an equality restriction). Most recently:
https://github.com/racket/racket/issues/3425

- Jon

Jon Zeppieri

unread,
Dec 31, 2020, 2:12:00 PM12/31/20
to David Storrs, Racket Users
Right... except that I completely misread your first example, which is
not at all the same as my example with `cons` patterns. Sorry about
that.
Reply all
Reply to author
Forward
0 new messages