Списки

В данной статье рассказывается про списки. Списки это базовая компонента языка lisp. Именно для обработки списков был разработан lisp. Другие структуры данных появились позже. Однако, так ли просты списки как это кажется на первый взгляд? Об этом вы узнаете в этой статье.

Списки

На самом деле список это не монолитная структура как массив. Это и очевидно. Во многих языках программирования есть такая структура данных как список и состоит она из ячеек или элементов списка, которые ссылаются друг на друга. Другими словами базовой структурой данных является пара ссылок или cons-ячейка. Одна из этих ссылок определяет данные — ссылается на них. Другая на следующую ячейку, либо на nil. Из таких структур можно строить не только списки, но и, например, деревья. Для работы с такими парами в lisp существует свой набор функций:

(cons 1 2)       ; (1 . 2)
(car (cons 1 2)) ; 1
(cdr (cons 1 2)) ; 2

(cons 1 nil)                   ; (1)
(cons 1 (cons 2 nil))          ; (1 2)
(cons 1 (cons 2 (cons 3 nil))) ; (1 2 3)

Возможно с знанием об этих ячейках будет проще понять статью про обработку коллекций. Кстати, всего существует 28 c*r функций. Между c и r можно вставить до 4 символов — либо a, либо d. Например cadadr.

Обработка списков

Название Обязательные аргументы Возвращаемое значение
COUNT Объект и последовательность Число вхождений в последовательности
FIND Объект и последовательность Объект или NIL
POSITION Объект и последовательность Индекс ячейки в последовательности или NI
REMOVE Удаляемый объект и последовательность Последовательность, из которой удалены указанные объекты
SUBSTITUTE Новый объект, заменяемый объект Последовательность, в которой указанные и последовательность объекты заменены на новые

Примеры вызова этих функций

(count 1 #(1 2 1 2 3 1 2 3 4))  ; 3
(remove 1 #(1 2 1 2 3 1 2 3 4)) ; #(2 2 3 2 3 4)
(remove 1 '(1 2 1 2 3 1 2 3 4)) ; (2 2 3 2 3 4)
(remove #\a "foobarbaz")        ; foobrbz

(substitute 10 1 #(1 2 1 2 3 1 2 3 4)) ; #(10 2 10 2 3 10 2 3 4)
(substitute 10 1 '(1 2 1 2 3 1 2 3 4)) ; (10 2 10 2 3 10 2 3 4)
(substitute #\x #\b "foobarbaz")       ; "fooxarxaz"
(find 1 #(1 2 1 2 3 1 2 3 4))          ; 1
(find 10 #(1 2 1 2 3 1 2 3 4))         ; NIL
(position 1 #(1 2 1 2 3 1 2 3 4))      ; 0

Функции высшего порядка

Здесь я уже не буду приводить много примеров. Смысл в том, что к функциям предыдущего размера (например count), добавляются постфиксы -if, -if-not. При вызове таких функций одним из аргументов передаётся функция условия. И есть ещё одна функция — remove-duplicates. Ей ничего лишнего не передаётся, она просто удаляет повторяющиеся значения. Вот так это работает:

(count-if #'evenp #(1 2 3 4 5))          ; 2 чётных
(count-if-not #'evenp #(1 2 3 4 5))      ; 3 нечётных
(remove-duplicates #(1 2 1 2 3 1 2 3 4)) ; #(1 2 3 4)

Работа со всей последовательностью

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

(concatenate 'vector #(1 2 3) '(4 5 6))         ; #(1 2 3 4 5 6)
(concatenate 'list #(1 2 3) '(4 5 6))           ; (1 2 3 4 5 6)
(concatenate 'string "abc" '(#\d #\e #\f))      ; "abcdef"
(sort (vector "foo" "bar" "baz") #'string<)     ; #("bar" "baz" "foo")
(setf my-sequence (sort my-sequence #'string<)) ; Не забывайте сохранять результат сортировки
(merge 'vector #(1 3 5) #(2 4 6) #'<)           ; #(1 2 3 4 5 6)

Работа с частью последовательности

Теперь несколько функций для работы с частями последовательностей:

(defparameter *x* (copy-seq "foobarbaz"))
(setf (subseq *x* 3 6) "xxx")  ; "fooxxxbaz"
(setf (subseq *x* 3 6) "abcd") ; "fooabcbaz" (вставилось не всё)
(setf (subseq *x* 3 6) "xx")   ; "fooxxcbaz"

(position #\b "foobarbaz") ; 3 ищет character
(search "bar" "foobarbaz") ; 3 ищет строку

(mismatch "foobarbaz" "foom") ; 3 начал несовпадать с 3-го элемента
(mismatch "foobar" "bar" :from-end t) ; 3, то же но с конца

Предикаты

Кроме этих функций есть предикаты:

(every #'evenp #(1 2 3 4 5))    ; NIL
(some #'evenp #(1 2 3 4 5))     ; T
(notany #'evenp #(1 2 3 4 5))   ; NIL
(notevery #'evenp #(1 2 3 4 5)) ; T

(every #'> #(1 2 3 4) #(5 4 3 2))    ; NIL
(some #'> #(1 2 3 4) #(5 4 3 2))     ; T
(notany #'> #(1 2 3 4) #(5 4 3 2))   ; NIL
(notevery #'> #(1 2 3 4) #(5 4 3 2)) ; T

Функции отображения

Последним примером функций высшего порядка являются функции отображения:

(map 'vector #'* #(1 2 3 4 5) #(10 9 8 7 6)) ; #(10 18 24 28 30)
(map-into a #'+ a b c) ; отображает последовательность в себя
(reduce #'+ #(1 2 3 4 5 6 7 8 9 10)) ; 55

Другие функции для обработки списков

Функция Описание
LAST Возвращает последнюю cons-ячейку в списке. Если вызывается с целочисленным аргументом n, возвращает n ячеек.
BUTLAST Возвращает копию списка без последней cons-ячейки. Если вызывается с целочисленным аргументом n, исключает последние n ячеек.
NBUTLAST Утилизирующая версия BUTLAST; может изменять переданный список-аргумент, не имеет строго заданных побочных эффектов.
LDIFF Возвращает копию списка до заданной cons-ячейки.
TAILP Возвращает TRUE если переданный объект является cons-ячейкой, которая является частью списка.
LIST* Строит список, содержащий все переданные аргументы кроме последнего, после этого присваивает CDR последней cons-ячейки списка последнему аргументу. Т.е. смесь LIST и APPEND.
MAKE-LIST Строит список из n элементов. Начальные элементы списка NIL или значение заданное аргументом :initial-element.
REVAPPEND Комбинация REVERSE и APPEND; инвертирует первый аргумент как REVERSE и добавляет второй аргумент. Не имеет строго заданных побочных эффектов.
NRECONC Утилизирующая версия предыдущей функции; инвертирует первый аргумент как это делает NREVERSE и добавляет второй аргумент. Не имеет строгих побочных эффектов.
CONSP Предикат для тестирования является ли объект cons-ячейкой.
ATOM Предикат для тестирования является ли объект не cons-ячейкой.
LISTP Предикат для тестирования является ли объект cons-ячейкой или NIL
NULL Предикат для тестирования является ли объект NIL. Функционально эквивалентен функции NOT, но стилистически лучше использовать NULL при тестировании является ли список NIL, NOT для проверки булевого выражения FALSE

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.