Hi, Racket Discourse.
I've been looking for a way to "filter" on certain patterns in lists in match
-forms, somewhat like list-no-order
, but more general in the sense that one can specify "indeterminate" arbitrarily distributed patterns as opposed to ones which you know the multiplicity of beforehand.
What I mean, is that I can't say something like (list-no-order (cons x 1) ... rest ...)
. From what I understand, list-no-order
is already "slow", for what that term's worth, so it makes sense.
What I've come up with, is the following:
I call the expander select
but filter
might be a better term.
(require (for-syntax racket/match
syntax/parse))
;; this is the expander I am referring to
(define-match-expander select
(lambda (stx)
(syntax-parse stx
[(select PAT)
(with-syntax ([ooo #'(... ...)])
#'(app (lambda (lst) (for/list ([elem lst] #:when (match elem [PAT #t] [_ #f])) elem))
(list PAT ooo)))])))
;; this is just a test of this for a particular use-case I am looking at
(define-match-expander hash-chain
(lambda (stx)
(syntax-parse stx
#:datum-literals (×)
[(hash-chain KEY × VAL)
#'(hash-table (KEY (select VAL)))]
[(hash-chain KEY × REST ...)
#'(hash-table (KEY (select (hash-chain REST ...))))]
[(hash-chain KEY VAL)
#'(hash-table (KEY VAL))]
[(hash-chain KEY REST ...)
#'(hash-table (KEY (hash-chain REST ...)))])))
(define-match-expander hash-chain-and
(lambda (stx)
(syntax-parse stx
[(hash-chain-and [CHAIN ...] ...)
#'(and (hash-chain CHAIN ...) ...)])))
(match #hash((tenants . (#hash((name . john) (title . dr)
(accounts . (#hash((name . work)
(owed . 20)))))
#hash((name . pete) (title . sir)
(accounts . (#hash((name . home)
(owed . 80))
#hash((name . work)
(owed . 21)))))
#hash((name . jeff) (title . mr)
(accounts . (#hash((name . home)
(owed . 100))
#hash((name . work)
(owed . 42)))))
#hash((name . john) (title . mr)
(accounts . (#hash((name . play)
(owed . 53))))))))
[(hash-chain
'tenants × (hash-chain-and
['title title]
['name (and name (not 'john))]
['accounts × (hash-chain-and
['owed (? (lambda (x) (< x 100)) owed)]
['name account-name])]))
(map list title name account-name owed)])
;; => '((sir pete (home work) (80 21)) (mr jeff (work) (42)))
I want to know if there is a "better" way to write select
. It is perfectly adequate (from what I can tell), but I am curious.
Edit: prettified the example code a bit.