2012-06-07 4 views

ответ

6

Существует set-difference функция в расширениях Common Lisp:

elisp> (require 'cl) 
cl 
elisp> (set-difference '(1 2 3) '(2 3 4)) 
(1) 
2

Отказ от ответственности: это не эффективный способ сделать это в Elisp. Эффективный способ через хэш-таблицу с хэш-функцией, но так как вы просили о списках, то вот оно:

(defun custom-set-difference (a b) 
    (remove-if 
    #'(lambda (x) (and (member x a) (member x b))) 
    (append a b))) 

(custom-set-difference '(1 2 3 4 5) '(2 4 6)) 

(1 3 5 6) 

(defun another-set-difference (a b) 
    (if (null a) b 
    (let (removed) 
     (labels ((find-and-remove 
       (c) 
       (cond 
       ((null c) nil) 
       ((equal (car c) (car a)) 
        (setq removed t) (cdr c)) 
       (t (cons (car c) (find-and-remove (cdr c))))))) 
     (setf b (find-and-remove b)) 
     (if removed 
      (another-set-difference (cdr a) b) 
      (cons (car a) (another-set-difference (cdr a) b))))))) 

(another-set-difference '(1 2 3 4 5) '(2 4 6)) 

(1 3 5 6) 

Второго чуть более эффективны, потому что он будет удалять элементы, как это делает последующее но первый - короче и более прямой.

Также обратите внимание, что списки не являются хорошим представлением множеств, потому что они, естественно, позволяют повторять. Хэш-карты лучше для этой цели.

2

Вот простое короткое определение &, которое должно быть понятным. Это по сути то же самое, что и функция set-difference в библиотеке Common Lisp для Emacs, но без какой-либо обработки аргумента TEST.

(defun set-diff (list1 list2 &optional key) 
    "Combine LIST1 and LIST2 using a set-difference operation. 
Optional arg KEY is a function used to extract the part of each list 
item to compare. 

The result list contains all items that appear in LIST1 but not LIST2. 
This is non-destructive; it makes a copy of the data if necessary, to 
avoid corrupting the original LIST1 and LIST2." 
    (if (or (null list1) (null list2)) 
     list1 
    (let ((keyed-list2 (and key (mapcar key list2))) 
      (result  ())) 
     (while list1 
     (unless (if key 
        (member (funcall key (car list1)) keyed-list2) 
        (member (car list1) list2)) 
      (setq result (cons (car list1) result))) 
     (setq list1 (cdr list1))) 
     result))) 
1

Когда я пишу Elisp код, который имеет множество список преобразований данных, я использую dash библиотеку, потому что она имеет огромное количество функций для работы со списками. Установленная разница может быть выполнена с помощью -difference:

(-difference '(1 2 3 4) '(3 4 5 6)) ;; => '(1 2)