15.5 Использование списков как множеств

Common Lisp содержит функции, которые позволяют обрабатывать списки элементов как множества. Сюда входят функции добавления, удаления и поиска элементов в списке, основанного на различных критериях. Кроме того, включены функции объединения, пересечения и разности.

Правила наименования данных функций и их именованных параметров в основном следуют правилам именования функций для последовательностей. Смотрите главу 14.

[Функция] member item list &key :test :test-not :key
[Функция] member-if predicate list &key :key
[Функция] member-if-not predicate list &key :key

Функция осуществляет поиск элемента, удовлетворяющего условию, в списке list. Если элемент не найдёт, возвращается nil. Иначе возвращается часть списка, начинающаяся с искомого элемента. Поиск осуществляется только в верхнем уровне списка. Эти функции могут использоваться в качестве предикатов.

Например:

(member ’snerd ’(a b c d))  nil
(member-if #’numberp ’(a #\Space 5/3 foo))  (5/3 foo)
(member ’a ’(g (a y) c a d e a f))  (a d e a f)

Следует отметить, что в последнем примере значение, возвращённое member, равно eq части списка, которая начинается на a. Если member вернула не nil значение, то для изменения полученного элемента списка можно использовать rplaca.

Смотрите также find и position.

X3J13 voted in January 1989 to restrict user side effects; see section 7.9.

Пользователь ограничен в создании побочных действий так, как это описано в разделе 7.9


[Функция] tailp sublist list

tailp истинен тогда и только тогда, когда существует такое целое число n, что выполняется

(eql sublist (nthcdr n list))

list может быть списком с точкой (подразумевается, что реализации могут использовать atom и не могут endp для проверки конца списка list). FIXME


[Функция] adjoin item list &key :test :test-not :key

adjoin используется для добавления элементов во множество, если этого элемента во множестве ещё не было. Условие равенства по-умолчанию eql.

(adjoin item list)  (if (member item list) list (cons item list))

Условие равенства может быть любым предикатом. item добавляется в список тогда и только тогда, когда в списке не было ни одного элемента, «удовлетворяющего условию».

adjoin отклоняется от обычных правил, описанных в главе 14 в части обработки параметров item и :key. Если указана :key функция, то она применяется к параметру item, также как и к каждому элементу списка. Обоснование в том, что если item ещё не был в списке и если он там появится, то применение функции :key к нему как элементу списка не будет корректным, если этого не было при его добавлении.

(adjoin item list :key fn)
   (if (member (funcall fn item) list :key fn) list (cons item list))

Смотрите также pushnew.

X3J13 voted in January 1989 to restrict user side effects; see section 7.9.

Пользователь ограничен в создании побочных действий так, как это описано в разделе 7.9


[Функция] union list1 list2 &key :test :test-not :key
[Функция] nunion list1 list2 &key :test :test-not :key

union принимает два списка и возвращает новый список, содержащий всё, что является элементами списков list1 и list2. Если в списках есть дубликаты, то в итоговом будет только один экземпляр. Например:

(union ’(a b c) ’(f a d))
    (a b c f d) или (b c f a d) или (d f a b c) или ...

(union ’((x 5) (y 6)) ’((z 2) (x 4)) :key #’car)
    ((x 5) (y 6) (z 2)) или ((x 4) (y 6) (z 2)) или ...

Порядок элементов в итоговом списке не обязательно совпадает с порядком соответствующих элементов в списках. Итоговый список может иметь общие ячейки с или быть равным eq переданным аргументам.

Функция :test может быть любым предикатом, и операция объединения может быть описана следующим образом. Для всех возможных упорядоченных пар, состоящих из одного элемента из списка list1 и одного элемента из списка list2, предикат устанавливает «равны» ли они. Для каждой пары равных элементов, как минимум один из двух элементов будет помещён в результат. Кроме того, любой элемент, которые не был равен ни одному другому элементу, также будет помещён в результат. Это описание может быть полезным при использовании хитрых функций проверки равенства.

Аргумент :test-not может быть полезен, когда функция проверки равенства является логическим отрицанием проверки равенства. Хороший пример такой функции это mismatch, которая логически инвертирована так, что если аргументы не равны, то может быть получена возможная полезная информация. Эта дополнительная «полезная информация» отбрасывается в следующем примере. mismatch используется только как предикат.

(union ’(#(a b) #(5 0 6) #(f 3))
       ’(#(5 0 6) (a b) #(g h))
       :test-not
       #’mismatch)
    (#(a b) #(5 0 6) #(f 3) #(g h))     ;Возможный результат
    ((a b) #(f 3) #(5 0 6) #(g h))      ;Другой возможный результат

Использование :test-not #’mismatch отличается от использования :test #’equalp, например, потому что mismatch определяет что #(a b) и (a b) одинаковы, тогда как equalp определяет эти выражения разными.

nunion является деструктивной версией union. Она выполняет ту же операцию, но может разрушить аргументы, возможно при использовании их ячеек для построения результата.

X3J13 voted in January 1989 to restrict user side effects; see section 7.9.

Пользователь ограничен в создании побочных действий так, как это описано в разделе 7.9

X3J13 voted in March 1989 to clarify the permissible side effects of certain operations; nunion is permitted to perform a setf on any part, car or cdr, of the top-level list structure of any of the argument lists.


[Функция] intersection list1 list2 &key :test :test-not :key
[Функция] nintersection list1 list2 &key :test :test-not :key

intersection принимает два списка и возвращает новый список содержащий все элементы, которые есть и в первом и во втором списках одновременно. Например:

(intersection ’(a b c) ’(f a d))  (a)

Порядок элементов в итоговом списке не обязательно совпадает с порядком соответствующих элементов в списках. Итоговый список может иметь общие ячейки с или быть равным eq переданным аргументам.

Функция :test может быть любым предикатом, и операция пересечения может быть описана следующим образом. Для всех возможных упорядоченных пар, состоящих из одного элемента из списка list1 и одного элемента из списка list2, предикат устанавливает «равны» ли они. Для каждой пары равных элементов, только один из двух элементов будет помещён в результат. Больше никаких элементов в итоговом списке не будет. Это описание может быть полезным при использовании хитрых функций проверки равенства.

nintersection является является деструктивной версией intersection. Она выполняет ту же операцию, но может разрушить аргумент list1, возможно при использовании их ячеек для построения результата. (Аргумент list2 не разрушается.)

X3J13 voted in January 1989 to restrict user side effects; see section 7.9.

Пользователь ограничен в создании побочных действий так, как это описано в разделе 7.9

X3J13 voted in March 1989 to clarify the permissible side effects of certain operations; nintersection is permitted to perform a setf on any part, car or cdr, of the top-level list structure of any of the argument lists.


[Функция] set-difference list1 list2 &key :test :test-not :key
[Функция] nset-difference list1 list2 &key :test :test-not :key

set-difference возвращает список элементов списка list1, которые не встречаются в списке list2. Данная операция не разрушает аргументы.

Порядок элементов в итоговом списке не обязательно совпадает с порядком соответствующих элементов в списке list1. Итоговый список может иметь общие ячейки, или быть равным eq аргументу list1.

:test может быть любым предикатом, и операция разности множеств может быть описана следующим образом. Для всех возможных упорядоченных пар, состоящих из элементов первого и второго списков, используется предикат для установки их «равенства». Элемент из списка list1 помещается в результат, тогда и только тогда, когда он не равен ни одному элементу списка. Это позволяет делать очень интересные приложения. Например, можно удалить из списка строк все строки, содержащие некоторый список символов: list2.

;; Удалить все имена специй содержащие буквы "c" или "w".
(set-difference ’("strawberry" "chocolate" "banana"
                  "lemon" "pistachio" "rhubarb")
                ’(#\c #\w)
                :test
                #’(lambda (s c) (find c s)))
    ("banana" "rhubarb" "lemon")     ;Возможен другой порядок элементов

nset-difference является деструктивной версией set-difference. Данная операция может разрушить list1.

X3J13 voted in January 1989 to restrict user side effects; see section 7.9.

Пользователь ограничен в создании побочных действий так, как это описано в разделе 7.9


[Функция] set-exclusive-or list1 list2 &key :test :test-not :key
[Функция] nset-exclusive-or list1 list2 &key :test :test-not :key

set-exclusive-or возвращает список элементов, которые встречаются только в списке list1 и только в списке list2. Данная операция не разрушает аргументы.

Порядок элементов в итоговом списке не обязательно совпадает с порядком соответствующих элементов в списке list1. Итоговый список может иметь общие ячейки, или быть равным eq аргументу list1.

Функция проверки равенства элементов может быть любым предикатом, и операцию set-exclusive-or можно описать следующим образом. Для всех возможных упорядоченных пар, содержащих один элемент из списка list1 и один элемент из списка list2, функция используется для проверки «равенства». Результат содержит точно те элементы списков list1 и list2, которые были только в различающихся парах.

nset-exclusive-or является деструктивной версией set-exclusive-or. Данная операция может разрушить аргументы.

X3J13 voted in January 1989 to restrict user side effects; see section 7.9.

Пользователь ограничен в создании побочных действий так, как это описано в разделе 7.9

X3J13 voted in March 1989 to clarify the permissible side effects of certain operations; nset-exclusive-or is permitted to perform a setf on any part, car or cdr, of the top-level list structure of any of the argument lists.


[Функция] subsetp list1 list2 &key :test :test-not :key

subsetp является предикатом, который истинен, если каждый элемент списка list1 встречается в («равен» некоторому элементу в) списке list2, иначе ложен.

X3J13 voted in January 1989 to restrict user side effects; see section 7.9.

Пользователь ограничен в создании побочных действий так, как это описано в разделе 7.9