Is there a way to check if all elements of a list

2019-03-05 10:27发布

问题:

I want a function that does something like this:

>(function '(1 2 3 4) '(1 2 3 4 5))
#t

When in this case is returning #t because all elements of the first list are contained in the second list. Is there a function that does that without having to worry about order?

回答1:

So as far as I know there is no built in function for this, and I believe the shortest way to define such function would be something like this.

(define (list-check l1 l2)
  (andmap (λ(x) (not (boolean? (memq x l2)))) l1))


回答2:

In this case, you aren't comparing the lists as lists, but as sets, so it would make much more sense to use a structure designed to be used that way. Racket provides hash sets in its standard library. This makes the function you're looking for trivial.

(define (list-subset? a b)
  (subset? (list->set a) (list->set b)))


回答3:

Using memq works swell on small lists, however since we have hash tables available we might as well use them:

(define (list-check-hash l1 l2)
  (define hash (make-hasheq))
  (for-each (λ (x) (hash-set! hash x #t)) l2)
  (andmap (λ (x) (hash-ref hash x #f)) l1))

;; test
(define test 
  (let loop ((x 1000000) (acc '()))
    (if (zero? x) acc (loop (sub1 x) (cons x acc)))))

(list-check-hash test test)       ;==> #t
(list-check-hash test (cdr test)) ;==> #f

Of course you may choose to use immutable ones instead since it's more idiomatic and still a lot faster than memq:

(define (list-check-hash l1 l2)
  (define hash (make-immutable-hasheq (map (λ (x) (cons x #t)) l2)))
  (andmap (λ (x) (hash-ref hash x #f)) l1))