简体   繁体   中英

In Racket how do I return a list that contains all the keys which occur in two different dictionaries

I am basically just trying to find keys that exist in two different dictionaries and input those keys into a list.

Here is my code so far

(define-struct asc (key val))
;; An Asc is a (make-asc Any Any)
;; a Dict (dictionary) is a (listof Asc)


(define (common-keys D1 D2)
  (cond
    [(or (empty? D1) (empty? D2)) '()]
    [(equal?  (asc-val (first D1)) (asc-val (first D2)))
 (cons (asc-key (first D1)))]
    [ else (cons (asc-key (first D1 (common-keys (rest D1) (rest D2)))))])) 

The code obviously doesn't work. My thought process for this question was to first to check to see if either dictionary is empty and then see if the first values in both dictionaries are equal, if they are, then I construct a list. I now need to add a section where it then iterates throughout the rest of the dictionaries to see whether the other keys are equal. I am unsure how to do this, I'm still inexperienced working with many lists at once so working with multiple dictionaries at once is a bit tricky for me.

This is a test case example

(check-expect (common-keys
               (list (make-asc 1 "one") (make-asc 15 "fifteen"))
               (list (make-asc 15 "fifteen") (make-asc 8 "eight")))
              (list 15)) 

since 15 is the only value in the two dictionaries the function should just return (list 15)

The problem with your approach is that you're assuming that the elements are on the same positions in both dictionaries, and that's not always the case. You'd have to check every key in one dictionary against all the other keys in the other dictionary, it'll be simpler if you build a helper procedure for this.

But wait! there's an easier solution if we think in terms of higher-order procedures. We just need to map over the keys of each dictionary, and then intersect the common elements. Easier done than said:

(define (common-keys D1 D2)
  (set-intersect
   (map asc-key D1)
   (map asc-key D2)))

It works as expected:

(common-keys
 (list (make-asc 1 "one") (make-asc 15 "fifteen"))
 (list (make-asc 15 "fifteen") (make-asc 8 "eight")))
=> '(15)

Longer example (just for educational purposes) with trail-recursive list-intersect.

Just to show you how to get it done with your original approach.

You have to first sort the list. And then go through both lists in parallel comparing the first elements of both lists. If they are equal, you collect them in the accumulator ( acc ). If not eliminate the smaller first element and apply list-intersect on the reduced and the not-reduced list ... until at least one of the lists or both are empty.

You need to specify therefore two test-functions:

  • test-equal for equality test of the keys and
  • test-less for testing whether one key is smaller than the other.
(define-struct asc (key val))

(define (list-intersect lst1 lst2 (acc '()) #:test-equal (test-equal =) #:test-less (test-less <))
  (let ((lst1 (sort lst1 test-less))
        (lst2 (sort lst2 test-less)))
    (cond ((or (empty? lst1) (empty? lst2)) (reverse acc))
          ((test-equal (car lst1) (car lst2)) 
           (list-intersect (cdr lst1) (cdr lst2) (cons (car lst1) acc)))
          ((test-less (car lst1) (car lst2))
           (list-intersect (cdr lst1) lst2 acc))
          (else
           (list-intersect lst1 (cdr lst2) acc)))))

(define (common-keys D1 D2 #:test-equal (test-equal =) #:test-less (test-less <))
  (list-intersect (map asc-key D1) (map asc-key D2) #:test-equal test-equal #:test-less test-less))
(common-keys (list (make-asc 1 "one") (make-asc 15 "fifteen"))
             (list (make-asc 15 "fifteen") (make-asc 8 "eight")))
;; '(15)

Improved by @Sylwester (using only test-less )

(define (list-intersect lst1 lst2 (acc '()) #:test-less (test-less <))
  (let ((lst1 (sort lst1 test-less))
        (lst2 (sort lst2 test-less)))
    (cond ((or (empty? lst1) (empty? lst2)) (reverse acc))
          ((test-less (car lst1) (car lst2))
           (list-intersect (cdr lst1) lst2 acc))
          ((test-less (car lst2) (car lst1))
           (list-intersect lst1 (cdr lst2) acc))
          (else
           (list-intersect (cdr lst1) (cdr lst2) (cons (car lst1) acc))))))

(define (common-keys D1 D2 #:test-less (test-less <))
  (list-intersect (map asc-key D1) (map asc-key D2) #:test-less test-less))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM