14.5 Сортировка и слияние

Эти функции могут модифицировать исходные данные: сортировать или соединять две уже отсортированные последовательности.

[Функция] sort sequence predicate &key :key
[Функция] stable-sort sequence predicate &key :key

Функция сортирует последовательность sequence в порядке, определяемом предикатом predicate. Результат помещается в исходную последовательность. Предикат predicate должен принимать два аргумента, возвращать не-nil тогда и только тогда, когда первый аргумент строго меньше второго (в подходящем для этого смысле). Если первый аргумент больше или равен второму (в подходящем для этого смысле), тогда предикат predicate должен вернуть nil.

Функция sort устанавливает отношение между двумя элементами с помощью предиката predicate, применённого к извлечённой из элемента ключевой части. Функция :key, применённая к элементу, должна возвращать его ключевую часть. Аргумент :key по-умолчанию равен функции эквивалентности, тем самым возвращая весь элемент.

Функция :key не должна иметь побочных эффектов. Полезным примером функции :key может быть функция-селектор для некоторой структуры (созданной с помощью defstruct), используемая для сортировки последовательности структур.

(sort a p :key s)  (sort a #’(lambda (x y) (p (s x) (s y))))

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

Если функции :key и predicate всегда возвращают управление, тогда операция сортировки будет всегда возвращать последовательность, содержащую такое же количество элементов как и в исходной (результат является просто последовательностью с переставленными элементами). Это поведение гарантируется, даже если предикат predicate в действительности не производит сравнение (в таком случае элементы будут расположены в неопределённом порядке, но ни один из них не будет потерян). Если функция :key последовательно возвращает необходимые ключевые части, и predicate отображает некоторый критерий для упорядочивания данных частей, тогда элементы итоговой последовательности будут корректно отсортированы в соответствие с этим критерием.

Операция сортировки, выполняемая с помощью sort, не гарантированно постоянна. Элементы, рассматриваемые предикатом predicate как равные, могут остаться или нет в оригинальном порядке. (Предполагается, что predicate рассматривает два элемента x и y равными, если (funcall predicate x y) и (funcall predicate y x) являются ложными.) Функция stable-sort гарантирует постоянность, но в некоторых ситуациях может быть медленнее чем sort.

Операция сортировки может быть деструктивное во всех случаях. В случае аргумента массива, она производится перестановкой элементов. В случае аргумента списка, список деструктивно переупорядочивается похожим образом как в nreverse. Таким образом, если аргумент не должен быть изменён, пользователь должен сортировать копию исходной последовательности.

Если применение функций :key или predicate вызывает ошибку, то состояние сортируемого массива или списка не определено. Однако, если ошибка может быть исправлена, сортировка, конечно же, будет завершена корректно.

Следует отметить, что сортировка требует много сравнений и следовательно много вызовов предиката predicate, сортировка будет более быстрой, если predicate является компилируемой, а не интерпретируемой функцией.

Например:

(setq foovector (sort foovector #’string-lessp :key #’car))

Если foovector содержит эти данные перед сортировкой

("Tokens" "The Lion Sleeps Tonight")
("Carpenters" "Close to You")
("Rolling Stones" "Brown Sugar")
("Beach Boys" "I Get Around")
("Mozart" "Eine Kleine Nachtmusik" (K 525))
("Beatles" "I Want to Hold Your Hand")

тогда после неё, foovector будет содержать

("Beach Boys" "I Get Around")
("Beatles" "I Want to Hold Your Hand")
("Carpenters" "Close to You")
("Mozart" "Eine Kleine Nachtmusik" (K 525))
("Rolling Stones" "Brown Sugar")
("Tokens" "The Lion Sleeps Tonight")

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

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


[Функция] merge result-type sequence1 sequence2 predicate &key :key

Функция деструктивно соединяет две последовательности sequence1 и sequence2 в порядке определяемом предикатом predicate. Результат является последовательностью типа result-type, который должен быть подтипом sequence. Предикат predicate должен принимать два аргумента, возвращать не-nil тогда и только тогда, когда первый аргумент строго меньше второго (в подходящем для этого смысле). Если первый аргумент больше или равен второму (в подходящем для этого смысле), тогда предикат predicate должен вернуть nil.

Функция merge устанавливает отношение между двумя элементами с помощью предиката predicate, применённого к извлечённой из элемента ключевой части. Функция :key, применённая к элементу, должна возвращать его ключевую часть. Аргумент :key по-умолчанию равен функции эквивалентности, тем самым возвращая весь элемент.

Функция :key не должна иметь побочных эффектов. Полезным примером функции :key может быть функция-селектор для некоторой структуры (созданной с помощью defstruct), используемая для сортировки последовательности структур.

Если функции :key и predicate всегда возвращают управление, тогда операция сортировки будет всегда завершаться. Результатом слияния двух последовательностей x и y является новая последовательность z, у которой длина равняется сумма длин x и y. z содержит все элементы x и y. Если x1 и x2 являются элементами x, и x1 стоит прежде x2, тогда в z x1 также будет стоять прежде чем x2. Такое же правило и для y. Если коротко, z является слиянием x и y.

Более того, если x и y были правильно отсортированы в соответствие с предикатом predicate, тогда z будет также правильно отсортирована. Например,

(merge ’list ’(1 3 4 6 7) ’(2 5 8) #’<)  (1 2 3 4 5 6 7 8)

Если x или y не были отсортированы, тогда z также не будет отсортирована. Однако слияние в любом случае произойдёт.

Операция слияния гарантированно постоянна. Если два или более элементов рассматриваются предикатом predicate как равные, тогда в результате элементы из sequence1 будут предшествовать элементам из sequence2. (Предполагается, что predicate рассматривает два элемента x и y равными, если (funcall predicate x y) и (funcall predicate y x) являются ложными.) Например:

(merge ’string "BOY" "nosy" #’char-lessp)  "BnOosYy"

Результат не может быть "BnoOsYy", "BnOosyY" или "BnoOsyY". Функция char-lessp игнорирует регистр, таким образом, например, символы Y и y равны. Свойство постоянности гарантирует, что символ из первого аргумента (Y) должен предшествовать символу из второго аргумента (y).

X3J13 voted in June 1989 to specify that merge should signal an error if the sequence type specifies the number of elements and the sum of the lengths of the two sequence arguments is different.

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

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