LISP - Краткое руководство

Джон Маккарти изобрел LISP в 1958 году, вскоре после разработки FORTRAN. Впервые он был реализован Стивом Расселом на компьютере IBM 704.

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

Common Lisp возник в 1980-х и 1990-х годах в попытке объединить работу нескольких групп реализации, которые были преемниками Maclisp, таких как ZetaLisp и NIL (Новая реализация Lisp) и т. Д.

Он служит общим языком, который можно легко расширить для конкретной реализации.

Программы, написанные на Common LISP, не зависят от машинно-зависимых характеристик, таких как длина слова и т. Д.

Особенности Common LISP

  • Машинно-независимый

  • Он использует методологию итеративного проектирования и простую расширяемость.

  • Это позволяет динамически обновлять программы.

  • Обеспечивает отладку на высоком уровне.

  • Он обеспечивает расширенное объектно-ориентированное программирование.

  • Предоставляет удобную систему макросов.

  • Он предоставляет широкий спектр типов данных, таких как объекты, структуры, списки, векторы, настраиваемые массивы, хеш-таблицы и символы.

  • Это основано на выражении.

  • Он обеспечивает объектно-ориентированную систему условий.

  • Он предоставляет полную библиотеку ввода-вывода.

  • Он предоставляет обширные структуры управления.

Приложения, встроенные в LISP

Крупные успешные приложения, созданные на Лиспе.

  • Emacs

  • G2

  • AutoCad

  • Игорь Гравер

  • Yahoo Store

Настройка локальной среды

Если вы все еще хотите настроить свою среду для языка программирования Lisp, вам понадобятся следующие два программного обеспечения, доступные на вашем компьютере: (а) текстовый редактор и (б) Lisp Executer.

Текстовый редактор

Это будет использоваться для ввода вашей программы. Примеры нескольких редакторов включают Блокнот Windows, команду редактирования ОС, Brief, Epsilon, EMACS и vim или vi.

Название и версия текстового редактора могут различаться в разных операционных системах. Например, Блокнот будет использоваться в Windows, а vim или vi можно использовать в Windows, а также в Linux или UNIX.

Файлы, которые вы создаете с помощью своего редактора, называются исходными файлами и содержат исходный код программы. Исходные файлы для программ на Лиспе обычно имеют расширение ".lisp".

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

Исполнитель Лиспа

Исходный код, записанный в исходном файле, является удобочитаемым источником вашей программы. Его нужно «выполнить», превратить в машинный язык, чтобы ваш ЦП мог действительно выполнять программу в соответствии с данными инструкциями.

Этот язык программирования Lisp будет использоваться для преобразования вашего исходного кода в окончательную исполняемую программу. Я предполагаю, что у вас есть базовые знания о языке программирования.

CLISP - это многоархитектурный компилятор GNU Common LISP, используемый для настройки LISP в Windows. Версия для Windows эмулирует среду unix с использованием MingW под Windows. Программа установки позаботится об этом и автоматически добавит clisp в переменную PATH Windows.

Вы можете получить последнюю версию CLISP для Windows отсюда - https://sourceforge.net/projects/clisp/files/latest/download

По умолчанию он создает ярлык в меню «Пуск» для построчного интерпретатора.

Как использовать CLISP

Во время установки clisp автоматически добавляется в вашу переменную PATH, если вы выбираете опцию (РЕКОМЕНДУЕТСЯ). Это означает, что вы можете просто открыть новое окно командной строки и набрать «clisp», чтобы вызвать компилятор.

Чтобы запустить файл * .lisp или * .lsp, просто используйте -

clisp hello.lisp

Выражения LISP называются символьными выражениями или s-выражениями. S-выражения состоят из трех допустимых объектов: атомов, списков и строк.

Любое s-выражение - допустимая программа.

LISP-программы работают либо на interpreter или как compiled code.

Интерпретатор проверяет исходный код в повторяющемся цикле, который также называется циклом чтения-оценки-печати (REPL). Он считывает программный код, оценивает его и распечатывает значения, возвращаемые программой.

Простая программа

Давайте напишем s-выражение, чтобы найти сумму трех чисел 7, 9 и 11. Для этого мы можем ввести текст в командной строке интерпретатора.

(+ 7 9 11)

LISP возвращает результат -

27

Если вы хотите запустить ту же программу в качестве скомпилированного кода, создайте файл исходного кода LISP с именем myprog.lisp и введите в него следующий код.

(write (+ 7 9 11))

Когда вы нажимаете кнопку «Выполнить» или нажимаете Ctrl + E, LISP выполняет его немедленно, и возвращается результат -

27

LISP использует префиксную нотацию

Вы могли заметить, что LISP использует prefix notation.

В приведенной выше программе символ + работает как имя функции для процесса суммирования чисел.

В префиксной записи операторы записываются перед своими операндами. Например, выражение,

a * ( b + c ) / d

будет записано как -

(/ (* a (+ b c) ) d)

Давайте возьмем еще один пример, давайте напишем код для преобразования температуры по Фаренгейту 60 o F в шкалу Цельсия -

Математическое выражение для этого преобразования будет -

(60 * 9 / 5) + 32

Создайте файл исходного кода с именем main.lisp и введите в него следующий код.

(write(+ (* (/ 9 5) 60) 32))

Когда вы нажимаете кнопку «Выполнить» или нажимаете Ctrl + E, LISP выполняет его немедленно и возвращает следующий результат:

140

Оценка программ LISP

Оценка программ LISP состоит из двух частей:

  • Перевод текста программы в объекты Lisp программой-читателем

  • Реализация семантики языка в терминах этих объектов программой-оценщиком

Процесс оценки включает следующие шаги -

  • Читатель переводит строки символов в объекты LISP или s-expressions.

  • Оценщик определяет синтаксис Лиспа formsкоторые построены из s-выражений. Этот второй уровень оценки определяет синтаксис, который определяет, какиеs-expressions являются формами LISP.

  • Оценщик работает как функция, которая принимает допустимую форму LISP в качестве аргумента и возвращает значение. Это причина, по которой мы помещаем выражение LISP в скобки, потому что мы отправляем все выражение / форму оценщику в качестве аргументов.

Программа "Hello World"

Изучение нового языка программирования не принесет успеха, пока вы не научитесь приветствовать весь мир на этом языке, верно!

Итак, создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(write-line "Hello World")

(write-line "I am at 'Tutorials Point'! Learning LISP")

Когда вы нажимаете кнопку «Выполнить» или нажимаете Ctrl + E, LISP выполняет его немедленно, и возвращается результат -

Hello World

I am at 'Tutorials Point'! Learning LISP

Основные строительные блоки в LISP

Программы LISP состоят из трех основных строительных блоков:

  • atom
  • list
  • string

An atomчисло или строка смежных символов. Он включает числа и специальные символы.

Ниже приведены примеры некоторых действительных атомов -

hello-from-tutorials-point
name
123008907
*hello*
Block#221
abc123

А list представляет собой последовательность атомов и / или других списков, заключенных в круглые скобки.

Ниже приведены примеры некоторых действительных списков -

( i am a list)
(a ( a b c) d e fgh)
(father tom ( susan bill joe))
(sun mon tue wed thur fri sat)
( )

А string - это группа символов, заключенная в двойные кавычки.

Ниже приведены примеры некоторых допустимых строк -

" I am a string"
"a ba c d efg #$%^&!"
"Please enter the following details :"
"Hello from 'Tutorials Point'! "

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

Символ точки с запятой (;) используется для обозначения строки комментария.

Например,

(write-line "Hello World") ; greet the world

; tell them your whereabouts

(write-line "I am at 'Tutorials Point'! Learning LISP")

Когда вы нажимаете кнопку «Выполнить» или нажимаете Ctrl + E, LISP выполняет его немедленно, и возвращается результат -

Hello World

I am at 'Tutorials Point'! Learning LISP

Некоторые важные моменты перед тем, как перейти к следующему

Ниже приведены некоторые важные моменты, на которые следует обратить внимание:

  • Основные числовые операции в LISP: +, -, * и /

  • LISP представляет вызов функции f (x) как (fx), например cos (45) записывается как cos 45

  • Выражения LISP нечувствительны к регистру, cos 45 или COS 45 одинаковы.

  • LISP пытается оценить все, включая аргументы функции. Только три типа элементов являются константами и всегда возвращают собственное значение

    • Numbers

    • Письмо t, это означает логическую истину.

    • Значение nil, что означает логическое ложное, а также пустой список.

Немного больше о формах LISP

В предыдущей главе мы упоминали, что процесс оценки кода LISP включает следующие шаги.

  • Читатель переводит строки символов в объекты LISP или s-expressions.

  • Оценщик определяет синтаксис Лиспа formsкоторые построены из s-выражений. Этот второй уровень оценки определяет синтаксис, который определяет, какие s-выражения являются формами LISP.

Теперь формы LISP могут быть.

  • Атом
  • Пустой или не включенный в список
  • Любой список, первым элементом которого является символ

Оценщик работает как функция, которая принимает допустимую форму LISP в качестве аргумента и возвращает значение. Это причина, по которой мы помещаемLISP expression in parenthesis, потому что мы отправляем все выражение / форму оценщику в качестве аргументов.

Соглашения об именах в LISP

Имя или символы могут состоять из любого количества буквенно-цифровых символов, кроме пробелов, открывающих и закрывающих круглых скобок, двойных и одинарных кавычек, обратной косой черты, запятой, двоеточия, точки с запятой и вертикальной черты. Чтобы использовать эти символы в имени, вам нужно использовать escape-символ (\).

Имя может состоять из цифр, но не целиком из цифр, потому что тогда оно будет читаться как число. Точно так же имя может иметь точки, но не может состоять полностью из точек.

Использование одинарных кавычек

LISP оценивает все, включая аргументы функции и члены списка.

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

Для этого нам нужно поставить перед атомом или списком одну кавычку.

Следующий пример демонстрирует это.

Создайте файл с именем main.lisp и введите в него следующий код.

(write-line "single quote used, it inhibits evaluation")
(write '(* 2 3))
(write-line " ")
(write-line "single quote not used, so expression evaluated")
(write (* 2 3))

Когда вы нажимаете кнопку «Выполнить» или нажимаете Ctrl + E, LISP выполняет его немедленно, и возвращается результат -

single quote used, it inhibits evaluation
(* 2 3) 
single quote not used, so expression evaluated
6

В LISP типизируются не переменные, а объекты данных.

Типы данных LISP можно разделить на категории.

  • Scalar types - например, типы чисел, буквы, символы и т. Д.

  • Data structures - например, списки, векторы, битовые векторы и строки.

Любая переменная может принимать в качестве значения любой объект LISP, если вы не заявили это явно.

Хотя указывать тип данных для переменной LISP необязательно, однако это помогает в некоторых расширениях цикла, в объявлениях методов и в некоторых других ситуациях, которые мы обсудим в следующих главах.

Типы данных организованы в иерархию. Тип данных - это набор объектов LISP, и многие объекты могут принадлежать одному такому набору.

В typep предикат используется для определения принадлежности объекта к определенному типу.

В type-of функция возвращает тип данных заданного объекта.

Спецификаторы типов в LISP

Спецификаторы типа - это системные символы для типов данных.

массив fixnum пакет простая строка
атом плавать путь простой вектор
bignum функция случайное состояние однопоплавковый
немного хеш-таблица соотношение стандартный символ
бит-вектор целое число рациональный поток
персонаж ключевое слово читаемый строка
[обычный] список последовательность [строка-символ]
скомпилированная функция длинный плавающий короткое плавание условное обозначение
сложный ниль подписанный байт т
минусы ноль простой массив беззнаковый байт
двойной поплавок количество простой бит-вектор вектор

Помимо этих типов, определенных системой, вы можете создавать свои собственные типы данных. Когда тип структуры определяется с помощьюdefstruct функции, имя типа структуры становится допустимым символом типа.

Пример 1

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(setq x 10)
(setq y 34.567)
(setq ch nil)
(setq n 123.78)
(setq bg 11.0e+4)
(setq r 124/2)

(print x)
(print y)
(print n)
(print ch)
(print bg)
(print r)

Когда вы нажимаете кнопку «Выполнить» или нажимаете Ctrl + E, LISP выполняет его немедленно, и возвращается результат -

10 
34.567 
123.78 
NIL 
110000.0 
62

Пример 2

Теперь давайте проверим типы переменных, использованных в предыдущем примере. Создайте новый файл исходного кода с именем main. lisp и введите в него следующий код.

(defvar x 10)
(defvar y 34.567)
(defvar ch nil)
(defvar n 123.78)
(defvar bg 11.0e+4)
(defvar r 124/2)

(print (type-of x))
(print (type-of y))
(print (type-of n))
(print (type-of ch))
(print (type-of bg))
(print (type-of r))

Когда вы нажимаете кнопку «Выполнить» или нажимаете Ctrl + E, LISP выполняет его немедленно, и возвращается результат -

(INTEGER 0 281474976710655) 
SINGLE-FLOAT 
SINGLE-FLOAT 
NULL 
SINGLE-FLOAT 
(INTEGER 0 281474976710655)

Макросы позволяют расширить синтаксис стандартного LISP.

Технически макрос - это функция, которая принимает s-выражение в качестве аргументов и возвращает форму LISP, которая затем оценивается.

Определение макроса

В LISP именованный макрос определяется с помощью другого макроса с именем defmacro. Синтаксис для определения макроса -

(defmacro macro-name (parameter-list))
"Optional documentation string."
body-form

Определение макроса состоит из имени макроса, списка параметров, необязательной строки документации и тела выражений Лиспа, которые определяют задание, которое должно выполняться макросом.

пример

Давайте напишем простой макрос с именем setTo10, который будет принимать число и устанавливать его значение равным 10.

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(defmacro setTo10(num)
(setq num 10)(print num))
(setq x 25)
(print x)
(setTo10 x)

Когда вы нажимаете кнопку «Выполнить» или нажимаете Ctrl + E, LISP выполняет его немедленно, и возвращается результат -

25
10

В LISP каждая переменная представлена symbol. Имя переменной - это имя символа, которое хранится в ячейке памяти символа.

Глобальные переменные

Глобальные переменные имеют постоянные значения во всей системе LISP и остаются в силе до тех пор, пока не будет указано новое значение.

Глобальные переменные обычно объявляются с использованием defvar построить.

Например

(defvar x 234)
(write x)

Когда вы нажимаете кнопку «Выполнить» или нажимаете Ctrl + E, LISP выполняет его немедленно, и возвращается результат.

234

Поскольку в LISP нет объявления типа для переменных, вы напрямую указываете значение для символа с помощью setq построить.

Например

->(setq x 10)

В приведенном выше выражении переменной x присваивается значение 10. Вы можете ссылаться на переменную, используя сам символ как выражение.

В symbol-value Функция позволяет извлечь значение, хранящееся в месте хранения символа.

Например

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(setq x 10)
(setq y 20)
(format t "x = ~2d y = ~2d ~%" x y)

(setq x 100)
(setq y 200)
(format t "x = ~2d y = ~2d" x y)

Когда вы нажимаете кнопку «Выполнить» или нажимаете Ctrl + E, LISP выполняет его немедленно, и возвращается результат.

x = 10 y = 20 
x = 100 y = 200

Локальные переменные

Локальные переменные определяются внутри данной процедуры. Параметры, названные в качестве аргументов в определении функции, также являются локальными переменными. Локальные переменные доступны только внутри соответствующей функции.

Как и глобальные переменные, локальные переменные также могут быть созданы с помощью setq построить.

Есть две другие конструкции - let и prog для создания локальных переменных.

Конструкция let имеет следующий синтаксис.

(let ((var1  val1) (var2  val2).. (varn  valn))<s-expressions>)

Где var1, var2, ..varn - имена переменных, а val1, val2, .. valn - начальные значения, присвоенные соответствующим переменным.

когда letвыполняется, каждой переменной присваивается соответствующее значение и, наконец, оценивается s-выражение . Возвращается значение последнего вычисленного выражения.

Если вы не укажете начальное значение для переменной, оно будет присвоено nil.

пример

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(let ((x 'a) (y 'b)(z 'c))
(format t "x = ~a y = ~a z = ~a" x y z))

Когда вы нажимаете кнопку «Выполнить» или нажимаете Ctrl + E, LISP выполняет его немедленно, и возвращается результат.

x = A y = B z = C

В prog конструкция также имеет список локальных переменных в качестве первого аргумента, за которым следует тело prog, и любое количество s-выражений.

В prog функция последовательно выполняет список s-выражений и возвращает ноль, если не встречает вызов функции с именем return. Тогда аргумент return функция оценивается и возвращается.

пример

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(prog ((x '(a b c))(y '(1 2 3))(z '(p q 10)))
(format t "x = ~a y = ~a z = ~a" x y z))

Когда вы нажимаете кнопку «Выполнить» или нажимаете Ctrl + E, LISP выполняет его немедленно, и возвращается результат.

x = (A B C) y = (1 2 3) z = (P Q 10)

В LISP константы - это переменные, которые никогда не меняют своих значений во время выполнения программы. Константы объявляются с использованиемdefconstant построить.

пример

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

В defun конструкция используется для определения функции, мы рассмотрим ее в Functions главу.

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(defconstant PI 3.141592)
(defun area-circle(rad)
   (terpri)
   (format t "Radius: ~5f" rad)
   (format t "~%Area: ~10f" (* PI rad rad)))
(area-circle 10)

Когда вы нажимаете кнопку «Выполнить» или нажимаете Ctrl + E, LISP выполняет его немедленно, и возвращается результат.

Radius:  10.0
Area:   314.1592

Оператор - это символ, который сообщает компилятору о необходимости выполнения определенных математических или логических операций. LISP позволяет выполнять множество операций с данными, поддерживаемых различными функциями, макросами и другими конструкциями.

Допустимые операции с данными можно разделить на следующие категории:

  • Арифметические операции
  • Операции сравнения
  • Логические операции
  • Побитовые операции

Арифметические операции

В следующей таблице показаны все арифметические операторы, поддерживаемые LISP. Предположим переменнуюA содержит 10 и переменную B держит 20, тогда -

Show Examples

Оператор Описание пример
+ Добавляет два операнда (+ AB) даст 30
- Вычитает второй операнд из первого (- AB) даст -10
* Умножает оба операнда (* AB) даст 200
/ Делит числитель на де-числитель (/ BA) даст 2
мод, rem Оператор модуля и остаток после целочисленного деления (мод BA) даст 0
incf Оператор увеличения увеличивает целочисленное значение на второй указанный аргумент (incf A 3) даст 13
decf Оператор Decrements уменьшает целочисленное значение на второй указанный аргумент (decf A 4) даст 9

Операции сравнения

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

Предположим переменную A содержит 10 и переменную B держит 20, то -

Show Examples

Оператор Описание пример
знак равно Проверяет, равны ли значения всех операндов или нет, если да, то условие становится истинным. (= AB) неверно.
знак равно Проверяет, все ли значения операндов разные или нет, если значения не равны, условие становится истинным. (/ = AB) верно.
> Проверяет, монотонно ли уменьшаются значения операндов. (> AB) не соответствует действительности.
< Проверяет, монотонно ли увеличиваются значения операндов. (<AB) верно.
> = Проверяет, больше ли значение любого левого операнда или равно значению следующего правого операнда, если да, то условие становится истинным. (> = AB) неверно.
<= Проверяет, меньше ли значение любого левого операнда или равно значению его правого операнда, если да, то условие становится истинным. (<= AB) верно.
Максимум Он сравнивает два или более аргумента и возвращает максимальное значение. (max AB) возвращает 20
мин Он сравнивает два или более аргумента и возвращает минимальное значение. (min AB) возвращает 10

Логические операции над логическими значениями

Общий LISP предоставляет три логических оператора: and, or, и notкоторый работает с логическими значениями. ПредполагатьA имеет значение ноль и B имеет значение 5, тогда -

Show Examples

Оператор Описание пример
и Требуется любое количество аргументов. Аргументы оцениваются слева направо. Если все аргументы не равны нулю, то возвращается значение последнего аргумента. В противном случае возвращается ноль. (и AB) вернет NIL.
или же Требуется любое количество аргументов. Аргументы оцениваются слева направо, пока один из них не станет отличным от нуля, в этом случае возвращается значение аргумента, в противном случае возвращаетсяnil. (или AB) вернет 5.
не Он принимает один аргумент и возвращает t если аргумент оценивается как nil. (не A) вернет T.

Побитовые операции с числами

Поразрядные операторы работают с битами и выполняют побитовые операции. Таблицы истинности для побитовых операций and, or и xor следующие:

Show Examples

п q p и q p или q p xor q
0 0 0 0 0
0 1 0 1 1
1 1 1 1 0
1 0 0 1 1
Assume if A = 60; and B = 13; now in binary format they will be as follows:
A = 0011 1100
B = 0000 1101
-----------------
A and B = 0000 1100
A or B = 0011 1101
A xor B = 0011 0001
not A  = 1100 0011

Побитовые операторы, поддерживаемые LISP, перечислены в следующей таблице. Предположим переменнуюA содержит 60 и переменную B имеет 13, тогда -

Оператор Описание пример
войти Это возвращает поразрядное логическое И его аргументов. Если аргумент не указан, результатом будет -1, что является идентификатором для этой операции. (logand ab)) даст 12
Logior Это возвращает поразрядное логическое ВКЛЮЧИТЕЛЬНОЕ ИЛИ его аргументов. Если аргумент не указан, результат равен нулю, что является идентификатором для этой операции. (logior ab) даст 61
logxor Это возвращает поразрядное логическое ИСКЛЮЧИТЕЛЬНОЕ ИЛИ его аргументов. Если аргумент не указан, результат равен нулю, что является идентификатором для этой операции. (logxor ab) даст 49
логнор Это возвращает побитовое НЕ своих аргументов. Если аргумент не указан, результатом будет -1, что является идентификатором для этой операции. (логнор аб) даст -62,
logeqv Это возвращает поразрядную логическую ЭКВИВАЛЕНТНОСТЬ (также известную как исключающее или) своих аргументов. Если аргумент не указан, результатом будет -1, что является идентификатором для этой операции. (logeqv ab) даст -50

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

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

LISP предоставляет следующие типы конструкций для принятия решений. Щелкните следующие ссылки, чтобы проверить их детали.

Sr.No. Конструкция и описание
1 cond

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

2 если

Конструкция if имеет различные формы. В простейшей форме за ним следует предложение проверки, действие теста и некоторые другие последующие действия. Если тестовое предложение оценивается как истинное, тогда тестовое действие выполняется, в противном случае оценивается последующее предложение.

3 когда

В простейшей форме за ним следует предложение test и действие test. Если тестовое предложение оценивается как истинное, тогда тестовое действие выполняется, в противном случае оценивается последующее предложение.

4 дело

Эта конструкция реализует несколько предложений тестовых действий, таких как конструкция cond. Однако он оценивает ключевую форму и допускает несколько предложений действий на основе оценки этой ключевой формы.

Возможна ситуация, когда вам нужно выполнить блок кодовых номеров несколько раз. Оператор цикла позволяет нам выполнять оператор или группу операторов несколько раз, и ниже приводится общая форма оператора цикла в большинстве языков программирования.

LISP предоставляет следующие типы конструкций для обработки требований цикла. Щелкните следующие ссылки, чтобы проверить их детали.

Sr.No. Конструкция и описание
1 петля

В loopконструкция - это простейшая форма итерации, предоставляемая LISP. В своей простейшей форме он позволяет многократно выполнять некоторые инструкции, пока не найдетreturn заявление.

2 петля для

Конструкция цикла for позволяет реализовать итерацию, подобную циклу for, которая наиболее распространена в других языках.

3 делать

Конструкция do также используется для выполнения итерации с использованием LISP. Он обеспечивает структурированную форму итерации.

4 время жизни

Конструкция dotimes позволяет выполнять цикл для некоторого фиксированного количества итераций.

5 долист

Конструкция dolist позволяет перебирать каждый элемент списка.

Изящный выход из блока

В block и return-from позволяет корректно выйти из любых вложенных блоков в случае какой-либо ошибки.

В blockФункция позволяет вам создать именованный блок с телом, состоящим из нуля или более операторов. Синтаксис -

(block block-name(
...
...
))

В return-from функция принимает имя блока и необязательное возвращаемое значение (по умолчанию - nil).

Следующий пример демонстрирует это -

пример

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код -

(defun demo-function (flag)
   (print 'entering-outer-block)
   
   (block outer-block
      (print 'entering-inner-block)
      (print (block inner-block

         (if flag
            (return-from outer-block 3)
            (return-from inner-block 5)
         )

         (print 'This-wil--not-be-printed))
      )

      (print 'left-inner-block)
      (print 'leaving-outer-block)
   t)
)
(demo-function t)
(terpri)
(demo-function nil)

Когда вы нажимаете кнопку «Выполнить» или нажимаете Ctrl + E, LISP выполняет его немедленно, и возвращается результат -

ENTERING-OUTER-BLOCK 
ENTERING-INNER-BLOCK 

ENTERING-OUTER-BLOCK 
ENTERING-INNER-BLOCK 
5 
LEFT-INNER-BLOCK 
LEAVING-OUTER-BLOCK

Функция - это группа операторов, которые вместе выполняют задачу.

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

Определение функций в LISP

Макрос с именем defunиспользуется для определения функций. Вdefun макросу нужны три аргумента -

  • Название функции
  • Параметры функции
  • Тело функции

Синтаксис для defun -

(defun name (parameter-list) "Optional documentation string." body)

Проиллюстрируем концепцию простыми примерами.

Пример 1

Напишем функцию с именем averagenum, которая будет печатать среднее четырех чисел. Мы отправим эти числа в качестве параметров.

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(defun averagenum (n1 n2 n3 n4)
   (/ ( + n1 n2 n3 n4) 4)
)
(write(averagenum 10 20 30 40))

Когда вы выполняете код, он возвращает следующий результат -

25

Пример 2

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

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(defun area-circle(rad)
   "Calculates area of a circle with given radius"
   (terpri)
   (format t "Radius: ~5f" rad)
   (format t "~%Area: ~10f" (* 3.141592 rad rad))
)
(area-circle 10)

Когда вы выполняете код, он возвращает следующий результат -

Radius:  10.0
Area:   314.1592

Обратите внимание, что -

  • В качестве параметров можно указать пустой список, что означает, что функция не принимает аргументов, список пуст и записывается как ().

  • LISP также допускает необязательные, множественные и ключевые аргументы.

  • Строка документации описывает назначение функции. Он связан с именем функции и может быть получен с помощьюdocumentation функция.

  • Тело функции может состоять из любого количества выражений Лиспа.

  • Значение последнего выражения в теле возвращается как значение функции.

  • Вы также можете вернуть значение из функции, используя return-from специальный оператор.

Давайте кратко обсудим приведенные выше концепции. Щелкните следующие ссылки, чтобы узнать подробности -

  • Дополнительные параметры

  • Остальные параметры

  • Параметры ключевого слова

  • Возврат значений из функции

  • Лямбда-функции

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

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

В следующей таблице показаны некоторые из наиболее часто используемых предикатов -

Sr.No. Предикат и описание
1

atom

Он принимает один аргумент и возвращает t, если аргумент является атомом, или ноль, если в противном случае.

2

equal

Он принимает два аргумента и возвращает t если они структурно равны или nil иначе.

3

eq

Он принимает два аргумента и возвращает t если это одни и те же идентичные объекты, совместно использующие одну и ту же ячейку памяти или nil иначе.

4

eql

Он принимает два аргумента и возвращает t если аргументы eq, или если это числа одного типа с одинаковым значением, или если они являются символьными объектами, которые представляют один и тот же символ, или nil иначе.

5

evenp

Он принимает один числовой аргумент и возвращает t если аргумент - четное число или nil в противном случае.

6

oddp

Он принимает один числовой аргумент и возвращает t если аргумент нечетное число или nil в противном случае.

7

zerop

Он принимает один числовой аргумент и возвращает t если аргумент равен нулю или nil в противном случае.

8

null

Он принимает один аргумент и возвращает t если аргумент равен нулю, иначе он возвращает nil.

9

listp

Он принимает один аргумент и возвращает t если аргумент оценивается как список, иначе он возвращает nil.

10

greaterp

Он принимает один или несколько аргументов и возвращает t если либо один аргумент, либо аргументы последовательно увеличиваются слева направо, либо nil в противном случае.

11

lessp

Он принимает один или несколько аргументов и возвращает t если либо один аргумент, либо аргументы последовательно уменьшаются слева направо, либо nil в противном случае.

12

numberp

Он принимает один аргумент и возвращает t если аргумент - число или nil в противном случае.

13

symbolp

Он принимает один аргумент и возвращает t если аргумент является символом, иначе он возвращает nil.

14

integerp

Он принимает один аргумент и возвращает t если аргумент является целым числом, иначе он возвращает nil.

15

rationalp

Он принимает один аргумент и возвращает t если аргумент - рациональное число, либо отношение, либо число, в противном случае возвращается nil.

16

floatp

Он принимает один аргумент и возвращает t если аргумент является числом с плавающей запятой, иначе он возвращает nil.

17

realp

Он принимает один аргумент и возвращает t если аргумент является действительным числом, иначе возвращается nil.

18

complexp

Он принимает один аргумент и возвращает t если аргумент - комплексное число, иначе возвращается nil.

19

characterp

Он принимает один аргумент и возвращает t если аргумент является символом, иначе он возвращает nil.

20

stringp

Он принимает один аргумент и возвращает t если аргумент является строковым объектом, иначе он возвращает nil.

21 год

arrayp

Он принимает один аргумент и возвращает t если аргумент является объектом массива, иначе он возвращает nil.

22

packagep

Он принимает один аргумент и возвращает t если аргумент - пакет, иначе он возвращает nil.

Пример 1

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(write (atom 'abcd))
(terpri)
(write (equal 'a 'b))
(terpri)
(write (evenp 10))
(terpri)
(write (evenp 7 ))
(terpri)
(write (oddp 7 ))
(terpri)
(write (zerop 0.0000000001))
(terpri)
(write (eq 3 3.0 ))
(terpri)
(write (equal 3 3.0 ))
(terpri)
(write (null nil ))

Когда вы выполняете код, он возвращает следующий результат -

T
NIL
T
NIL
T
NIL
NIL
NIL
T

Пример 2

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(defun factorial (num)
   (cond ((zerop num) 1)
      (t ( * num (factorial (- num 1))))
   )
)
(setq n 6)
(format t "~% Factorial ~d is: ~d" n (factorial n))

Когда вы выполняете код, он возвращает следующий результат -

Factorial 6 is: 720

Common Lisp определяет несколько видов чисел. Вnumber Тип данных включает различные типы чисел, поддерживаемые LISP.

LISP поддерживает следующие типы номеров:

  • Integers
  • Ratios
  • Числа с плавающей запятой
  • Сложные числа

На следующей диаграмме показана числовая иерархия и различные числовые типы данных, доступные в LISP.

Различные числовые типы в LISP

В следующей таблице описаны различные данные числового типа, доступные в LISP.

Sr.No. Тип данных и описание
1

fixnum

Этот тип данных представляет собой целые числа, которые не слишком велики и обычно находятся в диапазоне от -215 до 215-1 (это зависит от машины).

2

bignum

Это очень большие числа, размер которых ограничен объемом памяти, выделенной для LISP, это не фиксированные числа.

3

ratio

Представляет соотношение двух чисел в форме числитель / знаменатель. Функция / всегда дает результат в виде отношений, если ее аргументы являются целыми числами.

4

float

Он представляет собой нецелые числа. Существует четыре типа данных с плавающей запятой с возрастающей точностью.

5

complex

Он представляет собой комплексные числа, которые обозначаются #c. Реальная и мнимая части могут быть как рациональными числами, так и числами с плавающей запятой.

пример

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(write (/ 1 2))
(terpri)
(write ( + (/ 1 2) (/ 3 4)))
(terpri)
(write ( + #c( 1 2) #c( 3 -4)))

Когда вы выполняете код, он возвращает следующий результат -

1/2
5/4
#C(4 -2)

Числовые функции

В следующей таблице описаны некоторые часто используемые числовые функции -

Sr.No. Function & Description
1

+, -, *, /

Respective arithmetic operations

2

sin, cos, tan, acos, asin, atan

Respective trigonometric functions.

3

sinh, cosh, tanh, acosh, asinh, atanh

Respective hyperbolic functions.

4

exp

Exponentiation function. Calculates ex

5

expt

Exponentiation function, takes base and power both.

6

sqrt

It calculates the square root of a number.

7

log

Logarithmic function. It one parameter is given, then it calculates its natural logarithm, otherwise the second parameter is used as base.

8

conjugate

It calculates the complex conjugate of a number. In case of a real number, it returns the number itself.

9

abs

It returns the absolute value (or magnitude) of a number.

10

gcd

It calculates the greatest common divisor of the given numbers.

11

lcm

It calculates the least common multiple of the given numbers.

12

isqrt

It gives the greatest integer less than or equal to the exact square root of a given natural number.

13

floor, ceiling, truncate, round

All these functions take two arguments as a number and returns the quotient; floor returns the largest integer that is not greater than ratio, ceiling chooses the smaller integer that is larger than ratio, truncate chooses the integer of the same sign as ratio with the largest absolute value that is less than absolute value of ratio, and round chooses an integer that is closest to ratio.

14

ffloor, fceiling, ftruncate, fround

Does the same as above, but returns the quotient as a floating point number.

15

mod, rem

Returns the remainder in a division operation.

16

float

Converts a real number to a floating point number.

17

rational, rationalize

Converts a real number to rational number.

18

numerator, denominator

Returns the respective parts of a rational number.

19

realpart, imagpart

Returns the real and imaginary part of a complex number.

Example

Create a new source code file named main.lisp and type the following code in it.

(write (/ 45 78))
(terpri)
(write (floor 45 78))
(terpri)
(write (/ 3456 75))
(terpri)
(write (floor 3456 75))
(terpri)
(write (ceiling 3456 75))
(terpri)
(write (truncate 3456 75))
(terpri)
(write (round 3456 75))
(terpri)
(write (ffloor 3456 75))
(terpri)
(write (fceiling 3456 75))
(terpri)
(write (ftruncate 3456 75))
(terpri)
(write (fround 3456 75))
(terpri)
(write (mod 3456 75))
(terpri)
(setq c (complex 6 7))
(write c)
(terpri)
(write (complex 5 -9))
(terpri)
(write (realpart c))
(terpri)
(write (imagpart c))

When you execute the code, it returns the following result −

15/26
0
1152/25
46
47
46
46
46.0
47.0
46.0
46.0
6
#C(6 7)
#C(5 -9)
6
7

In LISP, characters are represented as data objects of type character.

You can denote a character object preceding #\ before the character itself. For example, #\a means the character a.

Space and other special characters can be denoted by preceding #\ before the name of the character. For example, #\SPACE represents the space character.

The following example demonstrates this −

Example

Create a new source code file named main.lisp and type the following code in it.

(write 'a)
(terpri)
(write #\a)
(terpri)
(write-char #\a)
(terpri)
(write-char 'a)

When you execute the code, it returns the following result −

A
#\a
a
*** - WRITE-CHAR: argument A is not a character

Special Characters

Common LISP allows using the following special characters in your code. They are called the semi-standard characters.

  • #\Backspace
  • #\Tab
  • #\Linefeed
  • #\Page
  • #\Return
  • #\Rubout

Character Comparison Functions

Numeric comparison functions and operators, like, < and > do not work on characters. Common LISP provides other two sets of functions for comparing characters in your code.

One set is case-sensitive and the other case-insensitive.

The following table provides the functions −

Case Sensitive Functions Case-insensitive Functions Description
char= char-equal Checks if the values of the operands are all equal or not, if yes then condition becomes true.
char/= char-not-equal Checks if the values of the operands are all different or not, if values are not equal then condition becomes true.
char< char-lessp Checks if the values of the operands are monotonically decreasing.
char> char-greaterp Checks if the values of the operands are monotonically increasing.
char<= char-not-greaterp Checks if the value of any left operand is greater than or equal to the value of next right operand, if yes then condition becomes true.
char>= char-not-lessp Checks if the value of any left operand is less than or equal to the value of its right operand, if yes then condition becomes true.

Example

Create a new source code file named main.lisp and type the following code in it.

; case-sensitive comparison
(write (char= #\a #\b))
(terpri)
(write (char= #\a #\a))
(terpri)
(write (char= #\a #\A))
(terpri)
   
;case-insensitive comparision
(write (char-equal #\a #\A))
(terpri)
(write (char-equal #\a #\b))
(terpri)
(write (char-lessp #\a #\b #\c))
(terpri)
(write (char-greaterp #\a #\b #\c))

When you execute the code, it returns the following result −

NIL
T
NIL
T
NIL
T
NIL

LISP allows you to define single or multiple-dimension arrays using the make-array function. An array can store any LISP object as its elements.

All arrays consist of contiguous memory locations. The lowest address corresponds to the first element and the highest address to the last element.

The number of dimensions of an array is called its rank.

In LISP, an array element is specified by a sequence of non-negative integer indices. The length of the sequence must equal the rank of the array. Indexing starts from zero.

For example, to create an array with 10- cells, named my-array, we can write −

(setf my-array (make-array '(10)))

The aref function allows accessing the contents of the cells. It takes two arguments, the name of the array and the index value.

For example, to access the content of the tenth cell, we write −

(aref my-array 9)

Example 1

Create a new source code file named main.lisp and type the following code in it.

(write (setf my-array (make-array '(10))))
(terpri)
(setf (aref my-array 0) 25)
(setf (aref my-array 1) 23)
(setf (aref my-array 2) 45)
(setf (aref my-array 3) 10)
(setf (aref my-array 4) 20)
(setf (aref my-array 5) 17)
(setf (aref my-array 6) 25)
(setf (aref my-array 7) 19)
(setf (aref my-array 8) 67)
(setf (aref my-array 9) 30)
(write my-array)

When you execute the code, it returns the following result −

#(NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL)
#(25 23 45 10 20 17 25 19 67 30)

Пример 2

Создадим массив 3 на 3.

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(setf x (make-array '(3 3) 
   :initial-contents '((0 1 2 ) (3 4 5) (6 7 8)))
)
(write x)

Когда вы выполняете код, он возвращает следующий результат -

#2A((0 1 2) (3 4 5) (6 7 8))

Пример 3

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(setq a (make-array '(4 3)))
(dotimes (i 4)
   (dotimes (j 3)
      (setf (aref a i j) (list i 'x j '= (* i j)))
   )
)
(dotimes (i 4)
   (dotimes (j 3)
      (print (aref a i j))
   )
)

Когда вы выполняете код, он возвращает следующий результат -

(0 X 0 = 0) 
(0 X 1 = 0) 
(0 X 2 = 0) 
(1 X 0 = 0) 
(1 X 1 = 1) 
(1 X 2 = 2) 
(2 X 0 = 0) 
(2 X 1 = 2) 
(2 X 2 = 4) 
(3 X 0 = 0) 
(3 X 1 = 3) 
(3 X 2 = 6)

Полный синтаксис для функции make-array

Функция make-array принимает множество других аргументов. Давайте посмотрим на полный синтаксис этой функции -

make-array dimensions :element-type :initial-element :initial-contents :adjustable :fill-pointer  :displaced-to :displaced-index-offset

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

Sr.No. Аргумент и описание
1

dimensions

Он дает размеры массива. Это число для одномерного массива и список для многомерного массива.

2

:element-type

Это спецификатор типа, значение по умолчанию - T, т.е. любой тип

3

:initial-element

Значение исходных элементов. Он создаст массив со всеми элементами, инициализированными определенным значением.

4

:initial-content

Исходный контент как объект.

5

:adjustable

Это помогает в создании изменяемого (или регулируемого) вектора, размер базовой памяти которого может быть изменен. Аргументом является логическое значение, указывающее, является ли массив регулируемым или нет, значение по умолчанию - NIL.

6

:fill-pointer

Он отслеживает количество элементов, фактически сохраненных в векторе изменяемого размера.

7

:displaced-to

Это помогает в создании смещенного массива или общего массива, который разделяет его содержимое с указанным массивом. Оба массива должны иметь один и тот же тип элементов. Параметр: displaced-to нельзя использовать с параметром: initial-element или: initial-contents. По умолчанию этот аргумент равен нулю.

8

:displaced-index-offset

Он дает смещение индекса созданного общего массива.

Пример 4

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(setq myarray (make-array '(3 2 3) 
   :initial-contents 
   '(((a b c) (1 2 3)) 
      ((d e f) (4 5 6)) 
      ((g h i) (7 8 9)) 
   ))
) 
(setq array2 (make-array 4 :displaced-to myarray :displaced-index-offset 2)) 
(write myarray)
(terpri)
(write array2)

Когда вы выполняете код, он возвращает следующий результат -

#3A(((A B C) (1 2 3)) ((D E F) (4 5 6)) ((G H I) (7 8 9)))
#(C 1 2 3)

Если смещенный массив двумерный -

(setq myarray (make-array '(3 2 3) 
   :initial-contents 
   '(((a b c) (1 2 3)) 
      ((d e f) (4 5 6)) 
      ((g h i) (7 8 9)) 
   ))
) 
(setq array2 (make-array '(3 2) :displaced-to myarray :displaced-index-offset 2)) 
(write myarray)
(terpri)
(write array2)

Когда вы выполняете код, он возвращает следующий результат -

#3A(((A B C) (1 2 3)) ((D E F) (4 5 6)) ((G H I) (7 8 9)))
#2A((C 1) (2 3) (D E))

Давайте изменим смещение смещенного индекса на 5 -

(setq myarray (make-array '(3 2 3) 
   :initial-contents 
   '(((a b c) (1 2 3)) 
      ((d e f) (4 5 6)) 
      ((g h i) (7 8 9)) 
   ))
) 
(setq array2 (make-array '(3 2) :displaced-to myarray :displaced-index-offset 5)) 
(write myarray)
(terpri)
(write array2)

Когда вы выполняете код, он возвращает следующий результат -

#3A(((A B C) (1 2 3)) ((D E F) (4 5 6)) ((G H I) (7 8 9)))
#2A((3 D) (E F) (4 5))

Пример 5

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

;a one dimensional array with 5 elements, 
;initail value 5
(write (make-array 5 :initial-element 5))
(terpri)

;two dimensional array, with initial element a
(write (make-array '(2 3) :initial-element 'a))
(terpri)

;an array of capacity 14, but fill pointer 5, is 5
(write(length (make-array 14 :fill-pointer 5)))
(terpri)

;however its length is 14
(write (array-dimensions (make-array 14 :fill-pointer 5)))
(terpri)

; a bit array with all initial elements set to 1
(write(make-array 10 :element-type 'bit :initial-element 1))
(terpri)

; a character array with all initial elements set to a
; is a string actually
(write(make-array 10 :element-type 'character :initial-element #\a)) 
(terpri)

; a two dimensional array with initial values a
(setq myarray (make-array '(2 2) :initial-element 'a :adjustable t))
(write myarray)
(terpri)

;readjusting the array
(adjust-array myarray '(1 3) :initial-element 'b) 
(write myarray)

Когда вы выполняете код, он возвращает следующий результат -

#(5 5 5 5 5)
#2A((A A A) (A A A))
5
(14)
#*1111111111
"aaaaaaaaaa"
#2A((A A) (A A))
#2A((A A B))

Строки в Common Lisp - это векторы, т.е. одномерный массив символов.

Строковые литералы заключаются в двойные кавычки. Любой символ, поддерживаемый набором символов, может быть заключен в двойные кавычки для создания строки, за исключением символа двойной кавычки (") и escape-символа (\). Однако вы можете включить их, экранировав их обратной косой чертой (\).

пример

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(write-line "Hello World")
(write-line "Welcome to Tutorials Point")

;escaping the double quote character
(write-line "Welcome to \"Tutorials Point\"")

Когда вы выполняете код, он возвращает следующий результат -

Hello World
Welcome to Tutorials Point
Welcome to "Tutorials Point"

Функции сравнения строк

Функции и операторы числового сравнения, например <и>, не работают со строками. Common LISP предоставляет два других набора функций для сравнения строк в вашем коде. Один набор чувствителен к регистру, а другой - без учета регистра.

В следующей таблице представлены функции -

Функции с учетом регистра Функции без учета регистра Описание
строка = равный по строке Проверяет, равны ли значения всех операндов или нет, если да, то условие становится истинным.
строка / = строка-не-равно Проверяет, все ли значения операндов разные или нет, если значения не равны, условие становится истинным.
строка < без струн Проверяет, монотонно ли уменьшаются значения операндов.
строка> струна-большая Проверяет, монотонно ли увеличиваются значения операндов.
строка <= строка-не-большая Проверяет, больше ли значение любого левого операнда или равно значению следующего правого операнда, если да, то условие становится истинным.
строка> = строка не менее Проверяет, меньше ли значение любого левого операнда или равно значению его правого операнда, если да, то условие становится истинным.

пример

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

; case-sensitive comparison
(write (string= "this is test" "This is test"))
(terpri)
(write (string> "this is test" "This is test"))
(terpri)
(write (string< "this is test" "This is test"))
(terpri)

;case-insensitive comparision
(write (string-equal "this is test" "This is test"))
(terpri)
(write (string-greaterp "this is test" "This is test"))
(terpri)
(write (string-lessp "this is test" "This is test"))
(terpri)

;checking non-equal
(write (string/= "this is test" "this is Test"))
(terpri)
(write (string-not-equal "this is test" "This is test"))
(terpri)
(write (string/= "lisp" "lisping"))
(terpri)
(write (string/= "decent" "decency"))

Когда вы выполняете код, он возвращает следующий результат -

NIL
0
NIL
T
NIL
NIL
8
NIL
4
5

Функции управления делами

В следующей таблице описаны функции управления делами -

Sr.No. Описание функции
1

string-upcase

Преобразует строку в верхний регистр

2

string-downcase

Преобразует строку в нижний регистр

3

string-capitalize

Делает каждое слово в строке заглавным

пример

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(write-line (string-upcase "a big hello from tutorials point"))
(write-line (string-capitalize "a big hello from tutorials point"))

Когда вы выполняете код, он возвращает следующий результат -

A BIG HELLO FROM TUTORIALS POINT
A Big Hello From Tutorials Point

Обрезка струн

В следующей таблице описаны функции обрезки строки -

Sr.No. Описание функции
1

string-trim

Он принимает строку символов в качестве первого аргумента и строку в качестве второго аргумента и возвращает подстроку, в которой все символы, содержащиеся в первом аргументе, удаляются из строки аргумента.

2

String-left-trim

Он принимает строку символов в качестве первого аргумента и строку в качестве второго аргумента и возвращает подстроку, в которой все символы, содержащиеся в первом аргументе, удалены из начала строки аргумента.

3

String-right-trim

Он принимает строковый символ (символы) в качестве первого аргумента и строку в качестве второго аргумента и возвращает подстроку, в которой все символы, содержащиеся в первом аргументе, удалены из конца строки аргумента.

пример

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(write-line (string-trim " " "   a big hello from tutorials point   "))
(write-line (string-left-trim " " "   a big hello from tutorials point   "))
(write-line (string-right-trim " " "   a big hello from tutorials point   "))
(write-line (string-trim " a" "   a big hello from tutorials point   "))

Когда вы выполняете код, он возвращает следующий результат -

a big hello from tutorials point
a big hello from tutorials point   
   a big hello from tutorials point
big hello from tutorials point

Другие строковые функции

Строки в LISP - это массивы, а значит, и последовательности. Мы рассмотрим эти типы данных в следующих руководствах. Все функции, применимые к массивам и последовательностям, также применимы к строкам. Однако мы продемонстрируем некоторые часто используемые функции на различных примерах.

Расчет длины

В length функция вычисляет длину строки.

Извлечение подстроки

В subseq функция возвращает подстроку (поскольку строка также является последовательностью), начиная с определенного индекса и продолжая до определенного конечного индекса или конца строки.

Доступ к символу в строке

В char функция позволяет получить доступ к отдельным символам строки.

Example

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(write (length "Hello World"))
(terpri)
(write-line (subseq "Hello World" 6))
(write (char "Hello World" 6))

Когда вы выполняете код, он возвращает следующий результат -

11
World
#\W

Сортировка и объединение строк

В sortфункция позволяет сортировать строку. Он принимает последовательность (вектор или строку) и предикат с двумя аргументами и возвращает отсортированную версию последовательности.

В merge Функция принимает две последовательности и предикат и возвращает последовательность, полученную путем слияния двух последовательностей в соответствии с предикатом.

Example

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

;sorting the strings
(write (sort (vector "Amal" "Akbar" "Anthony") #'string<))
(terpri)

;merging the strings
(write (merge 'vector (vector "Rishi" "Zara" "Priyanka") 
   (vector "Anju" "Anuj" "Avni") #'string<))

Когда вы выполняете код, он возвращает следующий результат -

#("Akbar" "Amal" "Anthony")
#("Anju" "Anuj" "Avni" "Rishi" "Zara" "Priyanka")

Переворачивание строки

В reverse функция переворачивает строку.

Например, создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(write-line (reverse "Are we not drawn onward, we few, drawn onward to new era"))

Когда вы выполняете код, он возвращает следующий результат -

are wen ot drawno nward ,wef ew ,drawno nward ton ew erA

Объединение строк

Функция конкатенации объединяет две строки. Это универсальная функция последовательности, и вы должны указать тип результата в качестве первого аргумента.

Например, создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(write-line (concatenate 'string "Are we not drawn onward, " "we few, drawn onward to new era"))

Когда вы выполняете код, он возвращает следующий результат -

Are we not drawn onward, we few, drawn onward to new era

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

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

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

Создание последовательности

Функция make-sequence позволяет создавать последовательность любого типа. Синтаксис этой функции -

make-sequence sqtype sqsize &key :initial-element

Он создает последовательность типа sqtype и длины sqsize.

При желании вы можете указать какое-либо значение, используя аргумент : initial-element , тогда каждый из элементов будет инициализирован этим значением.

Например, создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(write (make-sequence '(vector float) 
   10 
   :initial-element 1.0))

Когда вы выполняете код, он возвращает следующий результат -

#(1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0)

Общие функции на последовательностях

Sr.No. Описание функции
1

elt

Он позволяет получить доступ к отдельным элементам через целочисленный индекс.

2

length

Возвращает длину последовательности.

3

subseq

Он возвращает подпоследовательность, извлекая подпоследовательность, начиная с определенного индекса и продолжая до определенного конечного индекса или конца последовательности.

4

copy-seq

Он возвращает последовательность, содержащую те же элементы, что и его аргумент.

5

fill

Он используется для установки одного значения для нескольких элементов последовательности.

6

replace

Он принимает две последовательности, и первая последовательность аргументов деструктивно модифицируется путем копирования в нее последовательных элементов из второй последовательности аргументов.

7

count

Он принимает элемент и последовательность и возвращает количество раз, когда элемент встречается в последовательности.

8

reverse

Он возвращает последовательность, содержащую те же элементы аргумента, но в обратном порядке.

9

nreverse

Он возвращает ту же последовательность, содержащую те же элементы, что и последовательность, но в обратном порядке.

10

concatenate

Он создает новую последовательность, содержащую конкатенацию любого количества последовательностей.

11

position

Он принимает элемент и последовательность и возвращает индекс элемента в последовательности или ноль.

12

find

Требуется элемент и последовательность. Он находит элемент в последовательности и возвращает его, если не найден, возвращает ноль.

13

sort

Он принимает последовательность и предикат с двумя аргументами и возвращает отсортированную версию последовательности.

14

merge

Он принимает две последовательности и предикат и возвращает последовательность, полученную путем слияния двух последовательностей в соответствии с предикатом.

15

map

Он принимает функцию с n аргументами и n последовательностей и возвращает новую последовательность, содержащую результат применения функции к последующим элементам последовательностей.

16

some

Он принимает предикат в качестве аргумента и выполняет итерацию по последовательности аргументов и возвращает первое значение, отличное от NIL, возвращаемое предикатом, или возвращает false, если предикат никогда не выполняется.

17

every

Он принимает предикат в качестве аргумента и перебирает последовательность аргументов, он завершается, возвращая false, как только предикат терпит неудачу. Если предикат всегда выполняется, он возвращает истину.

18

notany

Он принимает предикат в качестве аргумента и выполняет итерацию по последовательности аргументов и возвращает false, как только предикат удовлетворяется, или true, если это не так.

19

notevery

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

20

reduce

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

21 год

search

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

22

remove

Он принимает элемент и последовательность и возвращает последовательность с удаленными экземплярами элемента.

23

delete

Это также принимает элемент и последовательность и возвращает последовательность того же типа, что и последовательность аргументов, которая имеет те же элементы, за исключением элемента.

24

substitute

Он принимает новый элемент, существующий элемент и последовательность и возвращает последовательность, в которой экземпляры существующего элемента заменены новым элементом.

25

nsubstitute

Он принимает новый элемент, существующий элемент и последовательность и возвращает ту же последовательность с экземплярами существующего элемента, замененными новым элементом.

26

mismatch

Он принимает две последовательности и возвращает индекс первой пары несовпадающих элементов.

Аргументы ключевого слова функции стандартной последовательности

Аргумент Имея в виду Значение по умолчанию
:контрольная работа Это функция с двумя аргументами, используемая для сравнения элемента (или значения, извлеченного функцией: key) с элементом. EQL
: ключ Функция с одним аргументом для извлечения ключевого значения из фактического элемента последовательности. NIL означает использование элемента как есть. Ноль
:Начало Начальный индекс (включительно) подпоследовательности. 0
:конец Конечный индекс (исключая) подпоследовательности. NIL означает конец последовательности. Ноль
: от конца Если true, последовательность будет пройдена в обратном порядке, от конца до начала. Ноль
: count Число, указывающее количество элементов, которые необходимо удалить или заменить, или NIL, чтобы указать все (только REMOVE и SUBSTITUTE). Ноль

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

Поиск длины и элемента

В length функция возвращает длину последовательности, а elt Функция позволяет получить доступ к отдельным элементам с помощью целочисленного индекса.

пример

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(setq x (vector 'a 'b 'c 'd 'e))
(write (length x))
(terpri)
(write (elt x 3))

Когда вы выполняете код, он возвращает следующий результат -

5
D

Изменение последовательностей

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

Следующий пример демонстрирует это -

Пример 1

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(write (count 7 '(1 5 6 7 8 9 2 7 3 4 5)))
(terpri)
(write (remove 5 '(1 5 6 7 8 9 2 7 3 4 5)))
(terpri)
(write (delete 5 '(1 5 6 7 8 9 2 7 3 4 5)))
(terpri)
(write (substitute 10 7 '(1 5 6 7 8 9 2 7 3 4 5)))
(terpri)
(write (find 7 '(1 5 6 7 8 9 2 7 3 4 5)))
(terpri)
(write (position 5 '(1 5 6 7 8 9 2 7 3 4 5)))

Когда вы выполняете код, он возвращает следующий результат -

2
(1 6 7 8 9 2 7 3 4)
(1 6 7 8 9 2 7 3 4)
(1 5 6 10 8 9 2 10 3 4 5)
7
1

Пример 2

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(write (delete-if #'oddp '(1 5 6 7 8 9 2 7 3 4 5)))
(terpri)
(write (delete-if #'evenp '(1 5 6 7 8 9 2 7 3 4 5)))
(terpri)
(write (remove-if #'evenp '(1 5 6 7 8 9 2 7 3 4 5) :count 1 :from-end t))
(terpri)
(setq x (vector 'a 'b 'c 'd 'e 'f 'g))
(fill x 'p :start 1 :end 4)
(write x)

Когда вы выполняете код, он возвращает следующий результат -

(6 8 2 4)
(1 5 7 9 7 3 5)
(1 5 6 7 8 9 2 7 3 5)
#(A P P P E F G)

Сортировка и объединение последовательностей

Функции сортировки принимают последовательность и предикат с двумя аргументами и возвращают отсортированную версию последовательности.

Пример 1

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(write (sort '(2 4 7 3 9 1 5 4 6 3 8) #'<))
(terpri)
(write (sort '(2 4 7 3 9 1 5 4 6 3 8) #'>))
(terpri)

Когда вы выполняете код, он возвращает следующий результат -

(1 2 3 3 4 4 5 6 7 8 9)
(9 8 7 6 5 4 4 3 3 2 1)

Пример 2

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(write (merge 'vector #(1 3 5) #(2 4 6) #'<))
(terpri)
(write (merge 'list #(1 3 5) #(2 4 6) #'<))
(terpri)

Когда вы выполняете код, он возвращает следующий результат -

#(1 2 3 4 5 6)
(1 2 3 4 5 6)

Предикаты последовательности

Функции every, some, notany и notevery называются предикатами последовательности.

Эти функции перебирают последовательности и проверяют логический предикат.

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

пример

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(write (every #'evenp #(2 4 6 8 10)))
(terpri)
(write (some #'evenp #(2 4 6 8 10 13 14)))
(terpri)
(write (every #'evenp #(2 4 6 8 10 13 14)))
(terpri)
(write (notany #'evenp #(2 4 6 8 10)))
(terpri)
(write (notevery #'evenp #(2 4 6 8 10 13 14)))
(terpri)

Когда вы выполняете код, он возвращает следующий результат -

T
T
NIL
NIL
T

Отображение последовательностей

Мы уже обсуждали функции отображения. Аналогичным образомmap Функция позволяет вам применять функцию к последующим элементам одной или нескольких последовательностей.

В map Функция принимает функцию с n аргументами и n последовательностей и возвращает новую последовательность после применения функции к последующим элементам последовательностей.

пример

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(write (map 'vector #'* #(2 3 4 5) #(3 5 4 8)))

Когда вы выполняете код, он возвращает следующий результат -

#(6 15 16 40)

Списки были самой важной и основной составной структурой данных в традиционном LISP. Современный Common LISP предоставляет другие структуры данных, такие как вектор, хеш-таблица, классы или структуры.

Списки представляют собой односвязные списки. В LISP списки строятся как цепочка простой структуры записи с именемcons связаны вместе.

Минусы Структура записи

А cons это структура записи, содержащая два компонента, называемых car и cdr.

Cons ячейки или cons - объекты - это пары значений, которые создаются с помощью функции cons.

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

Если второе значение не равно нулю или не является другой cons-ячейкой, тогда значения печатаются в виде пары с точками, заключенной в круглые скобки.

Два значения в cons-ячейке называются car и cdr. В car функция используется для доступа к первому значению и cdr функция используется для доступа ко второму значению.

пример

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(write (cons 1 2))
(terpri)
(write (cons 'a 'b))
(terpri)
(write (cons 1 nil))
(terpri)
(write (cons 1 (cons 2 nil)))
(terpri)
(write (cons 1 (cons 2 (cons 3 nil))))
(terpri)
(write (cons 'a (cons 'b (cons 'c nil))))
(terpri)
(write ( car (cons 'a (cons 'b (cons 'c nil)))))
(terpri)
(write ( cdr (cons 'a (cons 'b (cons 'c nil)))))

Когда вы выполняете код, он возвращает следующий результат -

(1 . 2)
(A . B)
(1)
(1 2)
(1 2 3)
(A B C)
A
(B C)

В приведенном выше примере показано, как структуры cons могут быть использованы для создания единого связанного списка, например, список (ABC) состоит из трех cons-ячеек, связанных вместе их компакт-дисками .

Схематически это можно было бы выразить как -

Списки в LISP

Хотя cons-ячейки можно использовать для создания списков, тем не менее, построение списка из вложенных consвызовы функций не могут быть лучшим решением. Вlist функция скорее используется для создания списков в LISP.

Функция списка может принимать любое количество аргументов, и, поскольку это функция, она оценивает свои аргументы.

В first и restфункции выдают первый элемент и остальную часть списка. Следующие примеры демонстрируют концепции.

Пример 1

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(write (list 1 2))
(terpri)
(write (list 'a 'b))
(terpri)
(write (list 1 nil))
(terpri)
(write (list 1 2 3))
(terpri)
(write (list 'a 'b 'c))
(terpri)
(write (list 3 4 'a (car '(b . c)) (* 4 -2)))
(terpri)
(write (list (list 'a 'b) (list 'c 'd 'e)))

Когда вы выполняете код, он возвращает следующий результат -

(1 2)
(A B)
(1 NIL)
(1 2 3)
(A B C)
(3 4 A B -8)
((A B) (C D E))

Пример 2

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(defun my-library (title author rating availability)
   (list :title title :author author :rating rating :availabilty availability)
)

(write (getf (my-library "Hunger Game" "Collins" 9 t) :title))

Когда вы выполняете код, он возвращает следующий результат -

"Hunger Game"

Список функций управления

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

Sr.No. Описание функции
1

car

Он принимает список в качестве аргумента и возвращает свой первый элемент.

2

cdr

Он принимает список в качестве аргумента и возвращает список без первого элемента.

3

cons

Он принимает два аргумента, элемент и список, и возвращает список с элементом, вставленным на первом месте.

4

list

Он принимает любое количество аргументов и возвращает список с аргументами в качестве элементов-членов списка.

5

append

Он объединяет два или более списка в один.

6

last

Он берет список и возвращает список, содержащий последний элемент.

7

member

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

8

reverse

Он берет список и возвращает список с верхними элементами в обратном порядке.

Обратите внимание, что все функции последовательности применимы к спискам.

Пример 3

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(write (car '(a b c d e f)))
(terpri)
(write (cdr '(a b c d e f)))
(terpri)
(write (cons 'a '(b c)))
(terpri)
(write (list 'a '(b c) '(e f)))
(terpri)
(write (append '(b c) '(e f) '(p q) '() '(g)))
(terpri)
(write (last '(a b c d (e f))))
(terpri)
(write (reverse '(a b c d (e f))))

Когда вы выполняете код, он возвращает следующий результат -

A
(B C D E F)
(A B C)
(A (B C) (E F))
(B C E F P Q G)
((E F))
((E F) D C B A)

Объединение функций car и cdr

В car и cdr функции и их комбинация позволяет извлечь любой конкретный элемент / член списка.

Однако последовательности функций car и cdr можно сократить, объединив букву a для car и d для cdr внутри букв c и r.

Например, мы можем написать cadadr, чтобы сократить последовательность вызовов функций - car cdr car cdr.

Таким образом, (cadadr '(a (cd) (efg))) вернет d

Пример 4

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(write (cadadr '(a (c d) (e f g))))
(terpri)
(write (caar (list (list 'a 'b) 'c)))   
(terpri)
(write (cadr (list (list 1 2) (list 3 4))))
(terpri)

Когда вы выполняете код, он возвращает следующий результат -

D
A
(3 4)

В LISP символ - это имя, которое представляет объекты данных, и что интересно, это также объект данных.

Что делает символы особенными, так это то, что они имеют компонент, называемый property list, или же plist.

Списки недвижимости

LISP позволяет вам назначать свойства символам. Например, у нас есть объект «человек». Нам бы хотелось, чтобы у этого объекта «лицо» были такие свойства, как имя, пол, рост, вес, адрес, профессия и т. Д. Свойство подобно имени атрибута.

Список свойств реализован как список с четным числом (возможно, нулевым) элементов. Каждая пара элементов в списке составляет запись; первый пункт - этоindicator, а второй - это value.

Когда символ создается, его список свойств изначально пуст. Свойства создаются с использованиемget в пределах setf форма.

Например, следующие операторы позволяют нам назначать свойства title, автора и издателя, а также соответствующие значения объекту с именем (символ) «книга».

Пример 1

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(write (setf (get 'books'title) '(Gone with the Wind)))
(terpri)
(write (setf (get 'books 'author) '(Margaret Michel)))
(terpri)
(write (setf (get 'books 'publisher) '(Warner Books)))

Когда вы выполняете код, он возвращает следующий результат -

(GONE WITH THE WIND)
(MARGARET MICHEL)
(WARNER BOOKS)

Различные функции списка свойств позволяют назначать свойства, а также извлекать, заменять или удалять свойства символа.

В getФункция возвращает список свойств символа для данного индикатора. Он имеет следующий синтаксис -

get symbol indicator &optional default

В getфункция ищет список свойств данного символа для указанного индикатора, если он найден, возвращает соответствующее значение; в противном случае возвращается значение по умолчанию (или ноль, если значение по умолчанию не указано).

Пример 2

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(setf (get 'books 'title) '(Gone with the Wind))
(setf (get 'books 'author) '(Margaret Micheal))
(setf (get 'books 'publisher) '(Warner Books))

(write (get 'books 'title))
(terpri)
(write (get 'books 'author))
(terpri)
(write (get 'books 'publisher))

Когда вы выполняете код, он возвращает следующий результат -

(GONE WITH THE WIND)
(MARGARET MICHEAL)
(WARNER BOOKS)

В symbol-plist функция позволяет увидеть все свойства символа.

Пример 3

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(setf (get 'annie 'age) 43)
(setf (get 'annie 'job) 'accountant)
(setf (get 'annie 'sex) 'female)
(setf (get 'annie 'children) 3)

(terpri)
(write (symbol-plist 'annie))

Когда вы выполняете код, он возвращает следующий результат -

(CHILDREN 3 SEX FEMALE JOB ACCOUNTANT AGE 43)

В remprop функция удаляет указанное свойство из символа.

Пример 4

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(setf (get 'annie 'age) 43)
(setf (get 'annie 'job) 'accountant)
(setf (get 'annie 'sex) 'female)
(setf (get 'annie 'children) 3)

(terpri)
(write (symbol-plist 'annie))
(remprop 'annie 'age)
(terpri)
(write (symbol-plist 'annie))

Когда вы выполняете код, он возвращает следующий результат -

(CHILDREN 3 SEX FEMALE JOB ACCOUNTANT AGE 43)
(CHILDREN 3 SEX FEMALE JOB ACCOUNTANT)

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

Создание векторов

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

Пример 1

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(setf v1 (vector 1 2 3 4 5))
(setf v2 #(a b c d e))
(setf v3 (vector 'p 'q 'r 's 't))

(write v1)
(terpri)
(write v2)
(terpri)
(write v3)

Когда вы выполняете код, он возвращает следующий результат -

#(1 2 3 4 5)
#(A B C D E)
#(P Q R S T)

Обратите внимание, что LISP использует синтаксис # (...) в качестве буквального обозначения векторов. Вы можете использовать этот синтаксис # (...) для создания и включения литеральных векторов в свой код.

Однако это буквальные векторы, поэтому их изменение в LISP не определено. Поэтому для программирования всегда следует использоватьvector функция, или более общая функция make-array для создания векторов, которые вы планируете модифицировать.

В make-arrayфункция - это более общий способ создания вектора. Вы можете получить доступ к элементам вектора, используяaref функция.

Пример 2

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(setq a (make-array 5 :initial-element 0))
(setq b (make-array 5 :initial-element 2))

(dotimes (i 5)
   (setf (aref a i) i))
   
(write a)
(terpri)
(write b)
(terpri)

Когда вы выполняете код, он возвращает следующий результат -

#(0 1 2 3 4)
#(2 2 2 2 2)

Указатель заполнения

В make-array функция позволяет создавать вектор изменяемого размера.

В fill-pointerАргумент функции отслеживает количество элементов, фактически сохраненных в векторе. Это индекс следующей позиции, которую нужно заполнить, когда вы добавляете элемент в вектор.

В vector-pushФункция позволяет вам добавить элемент в конец вектора изменяемого размера. Увеличивает указатель заливки на 1.

В vector-pop Функция возвращает последний отправленный элемент и уменьшает указатель заполнения на 1.

пример

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(setq a (make-array 5 :fill-pointer 0))
(write a)

(vector-push 'a a)
(vector-push 'b a)
(vector-push 'c a)

(terpri)
(write a)
(terpri)

(vector-push 'd a)
(vector-push 'e a)

;this will not be entered as the vector limit is 5
(vector-push 'f a)

(write a)
(terpri)

(vector-pop a)
(vector-pop a)
(vector-pop a)

(write a)

Когда вы выполняете код, он возвращает следующий результат -

#()
#(A B C)
#(A B C D E)
#(A B)

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

Common Lisp не предоставляет заданный тип данных. Однако он предоставляет ряд функций, которые позволяют выполнять операции над списком.

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

Реализация наборов в LISP

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

В adjoinфункция позволяет создавать набор. Он принимает элемент и список, представляющий набор, и возвращает список, представляющий набор, содержащий элемент и все элементы в исходном наборе.

В adjoinфункция сначала ищет элемент в данном списке, если он найден, то возвращает исходный список; в противном случае он создает новую cons-ячейку с ееcar как элемент и cdr указывая на исходный список и возвращает этот новый список.

В adjoin функция также принимает :key и :testаргументы ключевого слова. Эти аргументы используются для проверки того, присутствует ли элемент в исходном списке.

Поскольку функция adjoin не изменяет исходный список, чтобы внести изменения в сам список, вы должны либо присвоить значение, возвращаемое функцией adjoin, исходному списку, либо вы можете использовать макрос pushnew чтобы добавить предмет в набор.

пример

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

; creating myset as an empty list
(defparameter *myset* ())
(adjoin 1 *myset*)
(adjoin 2 *myset*)

; adjoin did not change the original set
;so it remains same
(write *myset*)
(terpri)
(setf *myset* (adjoin 1 *myset*))
(setf *myset* (adjoin 2 *myset*))

;now the original set is changed
(write *myset*)
(terpri)

;adding an existing value
(pushnew 2 *myset*)

;no duplicate allowed
(write *myset*)
(terpri)

;pushing a new value
(pushnew 3 *myset*)
(write *myset*)
(terpri)

Когда вы выполняете код, он возвращает следующий результат -

NIL
(2 1)
(2 1)
(3 2 1)

Проверка членства

Группа функций позволяет вам проверить, является ли элемент членом набора или нет.

Ниже приведены синтаксисы этих функций -

member item list &key :test :test-not :key 
member-if predicate list &key :key 
member-if-not predicate list &key :key

Эти функции ищут в заданном списке заданный элемент, удовлетворяющий тесту. Если такой элемент не найден, функция возвращаетnil. В противном случае возвращается конец списка с элементом в качестве первого элемента.

Поиск ведется только на верхнем уровне.

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

пример

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(write (member 'zara '(ayan abdul zara riyan nuha)))
(terpri)
(write (member-if #'evenp '(3 7 2 5/3 'a)))
(terpri)
(write (member-if-not #'numberp '(3 7 2 5/3 'a 'b 'c)))

Когда вы выполняете код, он возвращает следующий результат -

(ZARA RIYAN NUHA)
(2 5/3 'A)
('A 'B 'C)

Установить союз

Группа функций union позволяет выполнять объединение набора двух списков, предоставленных в качестве аргументов для этих функций, на основе теста.

Ниже приведены синтаксисы этих функций -

union list1 list2 &key :test :test-not :key 
nunion list1 list2 &key :test :test-not :key

В unionФункция принимает два списка и возвращает новый список, содержащий все элементы, присутствующие в любом из списков. Если есть дубликаты, то в возвращенном списке сохраняется только одна копия члена.

В nunion функция выполняет ту же операцию, но может уничтожить списки аргументов.

пример

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(setq set1 (union '(a b c) '(c d e)))
(setq set2 (union '(#(a b) #(5 6 7) #(f h)) 
   '(#(5 6 7) #(a b) #(g h)) :test-not #'mismatch)
)
       
(setq set3 (union '(#(a b) #(5 6 7) #(f h)) 
   '(#(5 6 7) #(a b) #(g h)))
)
(write set1)
(terpri)
(write set2)
(terpri)
(write set3)

Когда вы выполняете код, он возвращает следующий результат -

(A B C D E)
(#(F H) #(5 6 7) #(A B) #(G H))
(#(A B) #(5 6 7) #(F H) #(5 6 7) #(A B) #(G H))

Пожалуйста, обратите внимание

Функция объединения не работает должным образом без :test-not #'mismatchаргументы для списка из трех векторов. Это потому, что списки состоят из cons-ячеек, и хотя значения кажутся нам одинаковыми,cdrчасть ячеек не совпадает, поэтому они не совсем совпадают с интерпретатором / компилятором LISP. Это причина; не рекомендуется реализовывать большие наборы с использованием списков. Однако он отлично работает для небольших наборов.

Установить пересечение

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

Ниже приведены синтаксисы этих функций -

intersection list1 list2 &key :test :test-not :key 
nintersection list1 list2 &key :test :test-not :key

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

пример

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(setq set1 (intersection '(a b c) '(c d e)))
(setq set2 (intersection '(#(a b) #(5 6 7) #(f h)) 
   '(#(5 6 7) #(a b) #(g h)) :test-not #'mismatch)
)
       
(setq set3 (intersection '(#(a b) #(5 6 7) #(f h)) 
   '(#(5 6 7) #(a b) #(g h)))
)
(write set1)
(terpri)
(write set2)
(terpri)
(write set3)

Когда вы выполняете код, он возвращает следующий результат -

(C)
(#(A B) #(5 6 7))
NIL

Функция пересечения - это деструктивная версия пересечения, т. Е. Она может уничтожить исходные списки.

Установить разницу

Группа функций «набор-разность» позволяет вам выполнять разность наборов для двух списков, предоставленных в качестве аргументов этих функций на основе теста.

Ниже приведены синтаксисы этих функций -

set-difference list1 list2 &key :test :test-not :key 
nset-difference list1 list2 &key :test :test-not :key

Функция set-difference возвращает список элементов первого списка, которых нет во втором списке.

пример

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(setq set1 (set-difference '(a b c) '(c d e)))
(setq set2 (set-difference '(#(a b) #(5 6 7) #(f h)) 
   '(#(5 6 7) #(a b) #(g h)) :test-not #'mismatch)
)
(setq set3 (set-difference '(#(a b) #(5 6 7) #(f h)) 
   '(#(5 6 7) #(a b) #(g h)))
)
(write set1)
(terpri)
(write set2)
(terpri)
(write set3)

Когда вы выполняете код, он возвращает следующий результат -

(A B)
(#(F H))
(#(A B) #(5 6 7) #(F H))

Вы можете строить древовидные структуры данных из cons-ячеек в виде списков списков.

Чтобы реализовать древовидные структуры, вам нужно будет разработать функции, которые будут проходить через cons-ячейки в определенном порядке, например, предварительный заказ, порядок и последующий порядок для двоичных деревьев.

Дерево как список списков

Давайте рассмотрим древовидную структуру, состоящую из cons-ячеек, которые образуют следующий список списков:

((1 2) (3 4) (5 6)).

Схематически это можно было бы выразить как -

Древовидные функции в LISP

Хотя в большинстве случаев вам нужно будет написать свои собственные древовидные функции в соответствии с вашими конкретными потребностями, LISP предоставляет некоторые древовидные функции, которые вы можете использовать.

Помимо всех функций списка, следующие функции работают особенно с древовидными структурами:

Sr.No. Описание функции
1

copy-tree x & необязательный vecp

Он возвращает копию дерева cons-ячеек x. Он рекурсивно копирует как car, так и cdr направления. Если x не является cons-ячейкой, функция просто возвращает x без изменений. Если необязательный аргумент vecp равен true, эта функция копирует векторы (рекурсивно), а также cons-ячейки.

2

tree-equal xy & ключ: тест: тест-не: ключ

Он сравнивает два дерева cons-ячеек. Если x и y являются cons-ячейками, их автомобили и cdr сравниваются рекурсивно. Если ни x, ни y не являются cons-ячейкой, они сравниваются с помощью eql или в соответствии с указанным тестом. Функция: key, если она указана, применяется к элементам обоих деревьев.

3

subst новое старое дерево и ключ: test: test-not: key

Он заменяет вхождения данного старого элемента новым элементом в дереве , которое представляет собой дерево cons-ячеек.

4

nsubst новое старое дерево и ключ: test: test-not: key

Он работает так же, как subst, но уничтожает исходное дерево.

5

sublis дерево списка и ключ: test: test-not: key

Он работает как SUBST, за исключением того, что она принимает ассоциативный список ассоциативный список из старых-новых пар. Каждый элемент дерева (после применения функции: key, если она есть) сравнивается с автомобилями из alist; если он совпадает, он заменяется соответствующим cdr.

6

nsublis дерево списка и ключ: test: test-not: key

Работает так же, как sublis, но в деструктивной версии.

Пример 1

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(setq lst (list '(1 2) '(3 4) '(5 6)))
(setq mylst (copy-list lst))
(setq tr (copy-tree lst))

(write lst)
(terpri)
(write mylst)
(terpri)
(write tr)

Когда вы выполняете код, он возвращает следующий результат -

((1 2) (3 4) (5 6))
((1 2) (3 4) (5 6))
((1 2) (3 4) (5 6))

Пример 2

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(setq tr '((1 2 (3 4 5) ((7 8) (7 8 9)))))
(write tr)
(setq trs (subst 7 1 tr))
(terpri)
(write trs)

Когда вы выполняете код, он возвращает следующий результат -

((1 2 (3 4 5) ((7 8) (7 8 9))))
((7 2 (3 4 5) ((7 8) (7 8 9))))

Создание собственного дерева

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

Сначала давайте создадим новый узел, содержащий некоторые данные

(defun make-tree (item)
   "it creates a new node with item."
   (cons (cons item nil) nil)
)

Затем давайте добавим в дерево дочерний узел - он возьмет два узла дерева и добавит второе дерево как дочернее по отношению к первому.

(defun add-child (tree child)
   (setf (car tree) (append (car tree) child))
   tree)

Эта функция вернет первого дочернего элемента данного дерева - она ​​возьмет узел дерева и вернет первый дочерний элемент этого узла или ноль, если этот узел не имеет дочернего узла.

(defun first-child (tree)
   (if (null tree)
      nil
      (cdr (car tree))
   )
)

Эта функция вернет следующего одноуровневого узла данного узла - она ​​принимает узел дерева в качестве аргумента и возвращает ссылку на следующий одноуровневый узел или ноль, если узел не имеет его.

(defun next-sibling (tree)
   (cdr tree)
)

Наконец, нам нужна функция для возврата информации в узле -

(defun data (tree)
   (car (car tree))
)

пример

В этом примере используются вышеуказанные функции -

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(defun make-tree (item)
   "it creates a new node with item."
   (cons (cons item nil) nil)
)
(defun first-child (tree)
   (if (null tree)
      nil
      (cdr (car tree))
   )
)

(defun next-sibling (tree)
   (cdr tree)
)
(defun data (tree)
   (car (car tree))
)
(defun add-child (tree child)
   (setf (car tree) (append (car tree) child))
   tree
)

(setq tr '((1 2 (3 4 5) ((7 8) (7 8 9)))))
(setq mytree (make-tree 10))

(write (data mytree))
(terpri)
(write (first-child tr))
(terpri)
(setq newtree (add-child tr mytree))
(terpri)
(write newtree)

Когда вы выполняете код, он возвращает следующий результат -

10
(2 (3 4 5) ((7 8) (7 8 9)))

((1 2 (3 4 5) ((7 8) (7 8 9)) (10)))

Структура данных хэш-таблицы представляет собой набор key-and-valueпары, организованные на основе хэш-кода ключа. Он использует ключ для доступа к элементам в коллекции.

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

Создание хеш-таблицы в LISP

В Common LISP хеш-таблица - это коллекция общего назначения. Вы можете использовать произвольные объекты в качестве ключа или индексов.

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

В LISP хеш-таблицы можно разделить на три типа в зависимости от способа сравнения ключей - eq, eql или равные. Если хеш-таблица объектов LISP хешируется, ключи сравниваются с eq или eql. Если хеш-таблица хеш-таблицы имеет древовидную структуру, то она будет сравниваться с использованием равенства.

В make-hash-tableфункция используется для создания хеш-таблицы. Синтаксис этой функции -

make-hash-table &key :test :size :rehash-size :rehash-threshold

Где -

  • В key Аргумент предоставляет ключ.

  • В :testАргумент определяет, как сравниваются ключи - он должен иметь одно из трех значений # 'eq, #' eql или # 'равное или один из трех символов eq, eql или равный. Если не указано, предполагается eql.

  • В :sizeАргумент устанавливает начальный размер хеш-таблицы. Это должно быть целое число больше нуля.

  • В :rehash-sizeАргумент указывает, насколько увеличить размер хеш-таблицы, когда она станет полной. Это может быть целое число больше нуля, которое представляет собой количество добавляемых элементов, или число с плавающей запятой больше 1, которое представляет собой отношение нового размера к старому размеру. Значение по умолчанию для этого аргумента зависит от реализации.

  • В :rehash-thresholdАргумент указывает, насколько заполнится хеш-таблица, прежде чем она должна увеличиться. Это может быть целое число больше нуля и меньше: rehash-size (в этом случае оно будет масштабироваться при каждом увеличении таблицы), или это может быть число с плавающей запятой от нуля до 1. Значение по умолчанию для этого аргумент зависит от реализации.

Вы также можете вызвать функцию make-hash-table без аргументов.

Получение элементов из и добавление элементов в хеш-таблицу

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

Он имеет следующий синтаксис -

gethash key hash-table &optional default

где -

  • ключ: это связанный ключ

  • hash-table: хеш-таблица для поиска

  • по умолчанию: значение, которое должно быть возвращено, если запись не найдена, равное нулю, если не указано.

В gethash на самом деле функция возвращает два значения, второе - значение предиката, которое истинно, если запись была найдена, и ложь, если запись не найдена.

Для добавления элемента в хеш-таблицу вы можете использовать setf функции вместе с gethash функция.

пример

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(setq empList (make-hash-table)) 
(setf (gethash '001 empList) '(Charlie Brown))
(setf (gethash '002 empList) '(Freddie Seal)) 
(write (gethash '001 empList)) 
(terpri)
(write (gethash '002 empList))

Когда вы выполняете код, он возвращает следующий результат -

(CHARLIE BROWN)
(FREDDIE SEAL)

Удаление записи

В remhashфункция удаляет любую запись для определенного ключа в хеш-таблице. Это предикат, который истинен, если запись была, или ложна, если ее не было.

Синтаксис этой функции -

remhash key hash-table

пример

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(setq empList (make-hash-table)) 
(setf (gethash '001 empList) '(Charlie Brown))
(setf (gethash '002 empList) '(Freddie Seal)) 
(setf (gethash '003 empList) '(Mark Mongoose)) 

(write (gethash '001 empList)) 
(terpri)
(write (gethash '002 empList)) 
(terpri)
(write (gethash '003 empList))  
(remhash '003 empList)
(terpri)
(write (gethash '003 empList))

Когда вы выполняете код, он возвращает следующий результат -

(CHARLIE BROWN)
(FREDDIE SEAL)
(MARK MONGOOSE)
NIL

Функция maphash

В maphash Функция позволяет применить указанную функцию к каждой паре ключ-значение в хеш-таблице.

Он принимает два аргумента - функцию и хеш-таблицу и вызывает функцию один раз для каждой пары ключ / значение в хеш-таблице.

пример

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(setq empList (make-hash-table)) 
(setf (gethash '001 empList) '(Charlie Brown))
(setf (gethash '002 empList) '(Freddie Seal)) 
(setf (gethash '003 empList) '(Mark Mongoose)) 

(maphash #'(lambda (k v) (format t "~a => ~a~%" k v)) empList)

Когда вы выполняете код, он возвращает следующий результат -

3 => (MARK MONGOOSE)
2 => (FREDDIE SEAL)
1 => (CHARLIE BROWN)

Common LISP предоставляет множество функций ввода-вывода. Мы уже использовали функцию форматирования и функцию печати для вывода. В этом разделе мы рассмотрим некоторые из наиболее часто используемых функций ввода-вывода, представленных в LISP.

Функции ввода

В следующей таблице представлены наиболее часто используемые функции ввода LISP -

Sr.No. Описание функции
1

read& необязательный поток ввода eof-error-p eof-value recursive-p

Он считывает печатное представление объекта Lisp из потока ввода, строит соответствующий объект Lisp и возвращает объект.

2

read-preserving-whitespace& необязательный in-stream eof-error-p eof-value recursive-p

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

3

read-line& необязательный поток ввода eof-error-p eof-value recursive-p

Он читается в строке текста, оканчивающейся новой строкой.

4

read-char& необязательный поток ввода eof-error-p eof-value recursive-p

Он берет один символ из потока ввода и возвращает его как символьный объект.

5

unread-char символ и дополнительный поток ввода

Он помещает символ, который был недавно прочитан из потока ввода, в начало потока ввода.

6

peek-char& необязательный входной поток типа peek eof-error-p eof-value recursive-p

Он возвращает следующий символ, который будет считан из входного потока, без фактического удаления его из входного потока.

7

listen& необязательный поток ввода

Предикат listen истинно, если есть символ, сразу доступный из потока ввода, и ложно, если нет.

8

read-char-no-hang& необязательный поток ввода eof-error-p eof-value recursive-p

Это похоже на read-char, но если он не получает символ, он не ждет символа, а сразу же возвращает nil.

9

clear-input& необязательный поток ввода

Он очищает любой буферизованный ввод, связанный с потоком ввода.

10

read-from-string строка и необязательный eof-error-p eof-value & key: start: end: preserve-whitespace

Он последовательно принимает символы строки, строит объект LISP и возвращает этот объект. Он также возвращает индекс первого символа в непрочитанной строке или длину строки (или длину +1), в зависимости от обстоятельств.

11

parse-integer строка и ключ: начало: конец: основание: разрешено нежелательное

Он проверяет подстроку строки, разделенную: start и: end (по умолчанию это начало и конец строки). Он пропускает символы пробела, а затем пытается проанализировать целое число.

12

read-byte двоичный-вход-поток и необязательный eof-error-p eof-value

Он считывает один байт из потока двоичного ввода и возвращает его в виде целого числа.

Чтение ввода с клавиатуры

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

Например, рассмотрим фрагмент кода -

(write ( + 15.0 (read)))

Предположим, пользователь вводит 10.2 из ввода STDIN, он возвращает

25.2

Функция чтения считывает символы из входного потока и интерпретирует их путем синтаксического анализа как представления объектов Lisp.

пример

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код -

; the function AreaOfCircle
; calculates area of a circle
; when the radius is input from keyboard

(defun AreaOfCircle()
(terpri)
(princ "Enter Radius: ")
(setq radius (read))
(setq area (* 3.1416 radius radius))
(princ "Area: ")
(write area))
(AreaOfCircle)

Когда вы выполняете код, он возвращает следующий результат -

Enter Radius: 5 (STDIN Input)
Area: 78.53999

пример

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(with-input-from-string (stream "Welcome to Tutorials Point!")
   (print (read-char stream))
   (print (read-char stream))
   (print (read-char stream))
   (print (read-char stream))
   (print (read-char stream))
   (print (read-char stream))
   (print (read-char stream))
   (print (read-char stream))
   (print (read-char stream))
   (print (read-char stream))
   (print (peek-char nil stream nil 'the-end))
   (values)
)

Когда вы выполняете код, он возвращает следующий результат -

#\W 
#\e 
#\l 
#\c 
#\o 
#\m 
#\e 
#\Space 
#\t 
#\o 
#\Space

Функции вывода

Все функции вывода в LISP принимают необязательный аргумент, называемый потоком вывода, куда отправляется вывод. Если не указано или равно нулю, по умолчанию для потока вывода используется значение переменной * standard-output *.

В следующей таблице представлены наиболее часто используемые функции вывода LISP -

Sr.No. Функция и описание
1

write объект и ключ: поток: escape: radix: base: circle: pretty: level: length: case: gensym: array

write объект и ключ: поток: escape: radix: base: circle: pretty: level: length: case: gensym: array: readable: right-margin: miser-width: lines: pprint-dispatch

Оба записывают объект в выходной поток, указанный в: stream, который по умолчанию имеет значение * standard-output *. Другие значения по умолчанию равны соответствующим глобальным переменным, установленным для печати.

2

prin1 объект и дополнительный поток вывода

print объект и дополнительный поток вывода

pprint объект и дополнительный поток вывода

princ объект и дополнительный поток вывода

Все эти функции выводят печатное представление объекта в поток вывода . Однако есть следующие отличия -

  • prin1 возвращает объект в качестве своего значения.

  • print печатает объект с предыдущей новой строкой и пробелом. Он возвращает объект.

  • pprint аналогичен print, за исключением того, что в конце отсутствует пробел.

  • princ аналогичен prin1, за исключением того, что вывод не имеет escape-символа

3

write-to-string объект и ключ : escape: radix: base: circle: pretty: level: length: case: gensym: array

write-to-string объект и ключ: escape: radix: base: circle: pretty: level: length: case: gensym: array: readable: right-margin: miser-width: lines: pprint-dispatch

prin1-to-string объект

princ-to-string объект

Объект эффективно печатается, а выходные символы превращаются в строку, которая возвращается.

4

write-char символ и дополнительный поток вывода

Он выводит символ в поток вывода и возвращает символ.

5

write-string строка и необязательный поток вывода и ключ: начало: конец

Он записывает символы указанной подстроки строки в выходной поток.

6

write-line строка и необязательный поток вывода и ключ: начало: конец

Он работает так же, как write-string, но после этого выводит новую строку.

7

terpri& необязательный выходной поток

Он выводит новую строку в поток вывода.

8

fresh-line& необязательный выходной поток

он выводит новую строку, только если поток еще не находится в начале строки.

9

finish-output& необязательный выходной поток

force-output& необязательный выходной поток

clear-output& необязательный выходной поток

  • Функция finish-output пытается гарантировать, что весь вывод, отправленный в output-stream, достиг места назначения, и только после этого возвращает nil.

  • Функция force-output инициирует очистку всех внутренних буферов, но возвращает nil, не дожидаясь завершения или подтверждения.

  • Функция clear-output пытается прервать любую невыполненную операцию вывода в процессе, чтобы позволить как можно меньше выводить до места назначения.

10

write-byte целочисленный двоичный поток вывода

Он записывает один байт, значение целого числа.

пример

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

; this program inputs a numbers and doubles it
(defun DoubleNumber()
   (terpri)
   (princ "Enter Number : ")
   (setq n1 (read))
   (setq doubled (* 2.0 n1))
   (princ "The Number: ")
   (write n1)
   (terpri)
   (princ "The Number Doubled: ")
   (write doubled)
)
(DoubleNumber)

Когда вы выполняете код, он возвращает следующий результат -

Enter Number : 3456.78 (STDIN Input)
The Number: 3456.78
The Number Doubled: 6913.56

Форматированный вывод

Функция formatиспользуется для создания красиво отформатированного текста. Он имеет следующий синтаксис -

format destination control-string &rest arguments

где,

  • пункт назначения - стандартный вывод
  • control-string содержит символы для вывода и директиву печати.

А format directive состоит из тильды (~), необязательных параметров префикса, разделенных запятыми, необязательных модификаторов двоеточия (:) и знака at (@), а также одного символа, указывающего, что это за директива.

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

В следующей таблице приводится краткое описание обычно используемых директив -

Sr.No. Директива и описание
1

~A

За ним следуют аргументы ASCII.

2

~S

Сопровождается S-выражениями.

3

~D

Для десятичных аргументов.

4

~B

Для двоичных аргументов.

5

~O

Для восьмеричных аргументов.

6

~X

For hexadecimal arguments.

7

~C

For character arguments.

8

~F

For Fixed-format floating-point arguments.

9

~E

Exponential floating-point arguments.

10

~$

Dollar and floating point arguments.

11

~%

A new line is printed.

12

~*

Next argument is ignored.

13

~?

Indirection. The next argument must be a string, and the one after it a list.

Example

Let us rewrite the program calculating a circle's area −

Create a new source code file named main.lisp and type the following code in it.

(defun AreaOfCircle()
   (terpri)
   (princ "Enter Radius: ")
   (setq radius (read))
   (setq area (* 3.1416 radius radius))
   (format t "Radius: = ~F~% Area = ~F" radius area)
)
(AreaOfCircle)

When you execute the code, it returns the following result −

Enter Radius: 10.234 (STDIN Input)
Radius: = 10.234
Area = 329.03473

We have discussed about how standard input and output is handled by common LISP. All these functions work for reading from and writing into text and binary files too. Only difference is in this case the stream we use is not standard input or output, but a stream created for the specific purpose of writing into or reading from files.

In this chapter we will see how LISP can create, open, close text or binary files for their data storage.

A file represents a sequence of bytes, does not matter if it is a text file or binary file. This chapter will take you through important functions/macros for the file management.

Opening Files

You can use the open function to create a new file or to open an existing file. It is the most basic function for opening a file. However, the with-open-file is usually more convenient and more commonly used, as we will see later in this section.

When a file is opened, a stream object is constructed to represent it in the LISP environment. All operations on the stream are basically equivalent to operations on the file.

Syntax for the open function is −

open filename &key :direction :element-type :if-exists :if-does-not-exist :external-format

where,

  • The filename argument is the name of the file to be opened or created.

  • The keyword arguments specify the type of stream and error handling ways.

  • The :direction keyword specifies whether the stream should handle input, output, or both, it takes the following values −

    • :input - for input streams (default value)

    • :output - for output streams

    • :io - for bidirectional streams

    • :probe - for just checking a files existence; the stream is opened and then closed.

  • The :element-type specifies the type of the unit of transaction for the stream.

  • The :if-exists argument specifies the action to be taken if the :direction is :output or :io and a file of the specified name already exists. If the direction is :input or :probe, this argument is ignored. It takes the following values −

    • :error - it signals an error.

    • :new-version - it creates a new file with the same name but larger version number.

    • :rename - it renames the existing file.

    • :rename-and-delete - it renames the existing file and then deletes it.

    • :append - it appends to the existing file.

    • :supersede - it supersedes the existing file.

    • nil - it does not create a file or even a stream just returns nil to indicate failure.

  • The :if-does-not-exist argument specifies the action to be taken if a file of the specified name does not already exist. It takes the following values −

    • :error - it signals an error.

    • :create - it creates an empty file with the specified name and then uses it.

    • nil - it does not create a file or even a stream, but instead simply returns nil to indicate failure.

  • The :external-format argument specifies an implementation-recognized scheme for representing characters in files.

For example, you can open a file named myfile.txt stored in the /tmp folder as −

(open "/tmp/myfile.txt")

Writing to and Reading from Files

The with-open-file allows reading or writing into a file, using the stream variable associated with the read/write transaction. Once the job is done, it automatically closes the file. It is extremely convenient to use.

It has the following syntax −

with-open-file (stream filename {options}*)
   {declaration}* {form}*
  • filename is the name of the file to be opened; it may be a string, a pathname, or a stream.

  • The options are same as the keyword arguments to the function open.

Example 1

Create a new source code file named main.lisp and type the following code in it.

(with-open-file (stream "/tmp/myfile.txt" :direction :output)
   (format stream "Welcome to Tutorials Point!")
   (terpri stream)
   (format stream "This is a tutorials database")
   (terpri stream)
   (format stream "Submit your Tutorials, White Papers and Articles into our Tutorials   Directory.")
)

Please note that all input-output functions discussed in the previous chapter, such as, terpri and format are working for writing into the file we created here.

When you execute the code, it does not return anything; however, our data is written into the file. The :direction :output keywords allows us do this.

However, we can read from this file using the read-line function.

Example 2

Create a new source code file named main.lisp and type the following code in it.

(let ((in (open "/tmp/myfile.txt" :if-does-not-exist nil)))
   (when in
      (loop for line = (read-line in nil)
      
      while line do (format t "~a~%" line))
      (close in)
   )
)

When you execute the code, it returns the following result −

Welcome to Tutorials Point!
This is a tutorials database
Submit your Tutorials, White Papers and Articles into our Tutorials Directory.

Closing File

The close function closes a stream.

Structures are one of the user-defined data type, which allows you to combine data items of different kinds.

Structures are used to represent a record. Suppose you want to keep track of your books in a library. You might want to track the following attributes about each book −

  • Title
  • Author
  • Subject
  • Book ID

Defining a Structure

The defstruct macro in LISP allows you to define an abstract record structure. The defstruct statement defines a new data type, with more than one member for your program.

To discuss the format of the defstruct macro, let us write the definition of the Book structure. We could define the book structure as −

(defstruct book 
   title 
   author 
   subject 
   book-id 
)

Please note

  • The above declaration creates a book structure with four named components. So every book created will be an object of this structure.

  • It defines four functions named book-title, book-author, book-subject and book-book-id, which will take one argument, a book structure, and will return the fields title, author, subject and book-id of the book object. These functions are called the access functions.

  • The symbol book becomes a data type and you can check it using the typep predicate.

  • There will also be an implicit function named book-p, which is a predicate and will be true if its argument is a book and is false otherwise.

  • Another implicit function named make-book will be created, which is a constructor, which, when invoked, will create a data structure with four components, suitable for use with the access functions.

  • The #S syntax refers to a structure, and you can use it to read or print instances of a book.

  • An implicit function named copy-book of one argument is also defined that. It takes a book object and creates another book object, which is a copy of the first one. This function is called the copier function.

  • You can use setf to alter the components of a book, for example

(setf (book-book-id book3) 100)

Example

Create a new source code file named main.lisp and type the following code in it.

(defstruct book 
   title 
   author 
   subject 
   book-id 
)

( setq book1 (make-book :title "C Programming"
   :author "Nuha Ali" 
   :subject "C-Programming Tutorial"
   :book-id "478")
)

( setq book2 (make-book :title "Telecom Billing"
   :author "Zara Ali" 
   :subject "C-Programming Tutorial"
   :book-id "501")
) 

(write book1)
(terpri)
(write book2)
(setq book3( copy-book book1))
(setf (book-book-id book3) 100) 
(terpri)
(write book3)

When you execute the code, it returns the following result −

#S(BOOK :TITLE "C Programming" :AUTHOR "Nuha Ali" :SUBJECT "C-Programming Tutorial" :BOOK-ID "478")
#S(BOOK :TITLE "Telecom Billing" :AUTHOR "Zara Ali" :SUBJECT "C-Programming Tutorial" :BOOK-ID "501")
#S(BOOK :TITLE "C Programming" :AUTHOR "Nuha Ali" :SUBJECT "C-Programming Tutorial" :BOOK-ID 100)

In general term of programming languages, a package is designed for providing a way to keep one set of names separate from another. The symbols declared in one package will not conflict with the same symbols declared in another. This way packages reduce the naming conflicts between independent code modules.

The LISP reader maintains a table of all the symbols it has found. When it finds a new character sequence, it creates a new symbol and stores in the symbol table. This table is called a package.

The current package is referred by the special variable *package*.

There are two predefined packages in LISP −

  • common-lisp − it contains symbols for all the functions and variables defined.

  • common-lisp-user − it uses the common-lisp package and all other packages with editing and debugging tools; it is called cl-user in short

Package Functions in LISP

The following table provides most commonly used functions used for creating, using and manipulating packages −

Sr.No. Function and Description
1

make-package package-name &key :nicknames :use

It creates and returns a new package with the specified package name.

2

in-package package-name &key :nicknames :use

Makes the package current.

3

in-package name

This macro causes *package* to be set to the package named name, which must be a symbol or string.

4

find-package name

It searches for a package. The package with that name or nickname is returned; if no such package exists, find-package returns nil.

5

rename-package package new-name &optional new-nicknames

it renames a package.

6

list-all-packages

This function returns a list of all packages that currently exist in the Lisp system.

7

delete-package package

It deletes a package.

Creating a LISP Package

The defpackage function is used for creating an user defined package. It has the following syntax −

(defpackage :package-name
   (:use :common-lisp ...)
   (:export :symbol1 :symbol2 ...)
)

Where,

  • package-name is the name of the package.

  • The :use keyword specifies the packages that this package needs, i.e., packages that define functions used by code in this package.

  • The :export keyword specifies the symbols that are external in this package.

The make-package function is also used for creating a package. The syntax for this function is −

make-package package-name &key :nicknames :use

the arguments and keywords has same meaning as before.

Using a Package

Once you have created a package, you can use the code in this package, by making it the current package. The in-package macro makes a package current in the environment.

Example

Create a new source code file named main.lisp and type the following code in it.

(make-package :tom)
(make-package :dick)
(make-package :harry)
(in-package tom)
(defun hello () 
   (write-line "Hello! This is Tom's Tutorials Point")
)

(hello)
(in-package dick)
(defun hello () 
   (write-line "Hello! This is Dick's Tutorials Point")
)

(hello)
(in-package harry)
(defun hello () 
   (write-line "Hello! This is Harry's Tutorials Point")
)

(hello)
(in-package tom)
(hello)
(in-package dick)
(hello)
(in-package harry)
(hello)

When you execute the code, it returns the following result −

Hello! This is Tom's Tutorials Point
Hello! This is Dick's Tutorials Point
Hello! This is Harry's Tutorials Point

Deleting a Package

The delete-package macro allows you to delete a package. The following example demonstrates this −

Example

Create a new source code file named main.lisp and type the following code in it.

(make-package :tom)
(make-package :dick)
(make-package :harry)
(in-package tom)
(defun hello () 
   (write-line "Hello! This is Tom's Tutorials Point")
)

(in-package dick)
(defun hello () 
   (write-line "Hello! This is Dick's Tutorials Point")
)

(in-package harry)
(defun hello () 
   (write-line "Hello! This is Harry's Tutorials Point")
)

(in-package tom)
(hello)
(in-package dick)
(hello)
(in-package harry)
(hello)
(delete-package tom)
(in-package tom)
(hello)

When you execute the code, it returns the following result −

Hello! This is Tom's Tutorials Point
Hello! This is Dick's Tutorials Point
Hello! This is Harry's Tutorials Point
*** - EVAL: variable TOM has no value

In Common LISP terminology, exceptions are called conditions.

In fact, conditions are more general than exceptions in traditional programming languages, because a condition represents any occurrence, error, or not, which might affect various levels of function call stack.

Condition handling mechanism in LISP, handles such situations in such a way that conditions are used to signal warning (say by printing an warning) while the upper level code on the call stack can continue its work.

The condition handling system in LISP has three parts −

  • Signalling a condition
  • Handling the condition
  • Restart the process

Handling a Condition

Let us take up an example of handling a condition arising out of divide by zero condition, to explain the concepts here.

You need to take the following steps for handling a condition −

  • Define the Condition − "A condition is an object whose class indicates the general nature of the condition and whose instance data carries information about the details of the particular circumstances that lead to the condition being signalled".

    The define-condition macro is used for defining a condition, which has the following syntax −

    (define-condition condition-name (error)
       ((text :initarg :text :reader text))
    )

    New condition objects are created with MAKE-CONDITION macro, which initializes the slots of the new condition based on the :initargs argument.

    In our example, the following code defines the condition −

    (define-condition on-division-by-zero (error)
       ((message :initarg :message :reader message))
    )
  • Writing the Handlers − a condition handler is a code that are used for handling the condition signalled thereon. It is generally written in one of the higher level functions that call the erroring function. When a condition is signalled, the signalling mechanism searches for an appropriate handler based on the condition's class.

    Each handler consists of −

    • Type specifier, that indicates the type of condition it can handle
    • A function that takes a single argument, the condition

    When a condition is signalled, the signalling mechanism finds the most recently established handler that is compatible with the condition type and calls its function.

    The macro handler-case establishes a condition handler. The basic form of a handler-case −

    (handler-case expression error-clause*)

    Where, each error clause is of the form −

    condition-type ([var]) code)
  • Restarting Phase

    This is the code that actually recovers your program from errors, and condition handlers can then handle a condition by invoking an appropriate restart. The restart code is generally place in middle-level or low-level functions and the condition handlers are placed into the upper levels of the application.

    The handler-bind macro allows you to provide a restart function, and allows you to continue at the lower level functions without unwinding the function call stack. In other words, the flow of control will still be in the lower level function.

    The basic form of handler-bind is as follows −

    (handler-bind (binding*) form*)

    Where each binding is a list of the following −

    • a condition type
    • a handler function of one argument

    The invoke-restart macro finds and invokes the most recently bound restart function with the specified name as argument.

    You can have multiple restarts.

Example

In this example, we demonstrate the above concepts by writing a function named division-function, which will create an error condition if the divisor argument is zero. We have three anonymous functions that provide three ways to come out of it - by returning a value 1, by sending a divisor 2 and recalculating, or by returning 1.

Create a new source code file named main.lisp and type the following code in it.

(define-condition on-division-by-zero (error)
   ((message :initarg :message :reader message))
)
   
(defun handle-infinity ()
   (restart-case
      (let ((result 0))
         (setf result (division-function 10 0))
         (format t "Value: ~a~%" result)
      )
      (just-continue () nil)
   )
)
     
(defun division-function (value1 value2)
   (restart-case
      (if (/= value2 0)
         (/ value1 value2)
         (error 'on-division-by-zero :message "denominator is zero")
      )

      (return-zero () 0)
      (return-value (r) r)
      (recalc-using (d) (division-function value1 d))
   )
)

(defun high-level-code ()
   (handler-bind
      (
         (on-division-by-zero
            #'(lambda (c)
               (format t "error signaled: ~a~%" (message c))
               (invoke-restart 'return-zero)
            )
         )
         (handle-infinity)
      )
   )
)

(handler-bind
   (
      (on-division-by-zero
         #'(lambda (c)
            (format t "error signaled: ~a~%" (message c))
            (invoke-restart 'return-value 1)
         )
      )
   )
   (handle-infinity)
)

(handler-bind
   (
      (on-division-by-zero
         #'(lambda (c)
            (format t "error signaled: ~a~%" (message c))
            (invoke-restart 'recalc-using 2)
         )
      )
   )
   (handle-infinity)
)

(handler-bind
   (
      (on-division-by-zero
         #'(lambda (c)
            (format t "error signaled: ~a~%" (message c))
            (invoke-restart 'just-continue)
         )
      )
   )
   (handle-infinity)
)

(format t "Done."))

When you execute the code, it returns the following result −

error signaled: denominator is zero
Value: 1
error signaled: denominator is zero
Value: 5
error signaled: denominator is zero
Done.

Apart from the 'Condition System', as discussed above, Common LISP also provides various functions that may be called for signalling an error. Handling of an error, when signalled, is however, implementation-dependent.

Error Signalling Functions in LISP

The following table provides commonly used functions signalling warnings, breaks, non-fatal and fatal errors.

The user program specifies an error message (a string). The functions process this message and may/may not display it to the user.

The error messages should be constructed by applying the format function, should not contain a newline character at either the beginning or end, and need not indicate error, as the LISP system will take care of these according to its preferred style.

Sr.No. Function and Description
1

error format-string &rest args

It signals a fatal error. It is impossible to continue from this kind of error; thus error will never return to its caller.

2

cerror continue-format-string error-format-string &rest args

It signals an error and enters the debugger. However, it allows the program to be continued from the debugger after resolving the error.

3

warn format-string &rest args

it prints an error message but normally doesn't go into the debugger

4

break &optional format-string &rest args

It prints the message and goes directly into the debugger, without allowing any possibility of interception by programmed error-handling facilities

Example

In this example, the factorial function calculates factorial of a number; however, if the argument is negative, it raises an error condition.

Create a new source code file named main.lisp and type the following code in it.

(defun factorial (x)
   (cond ((or (not (typep x 'integer)) (minusp x))
      (error "~S is a negative number." x))
      ((zerop x) 1)
      (t (* x (factorial (- x 1))))
   )
)

(write(factorial 5))
(terpri)
(write(factorial -1))

When you execute the code, it returns the following result −

120
*** - -1 is a negative number.

Common LISP predated the advance of object-oriented programming by couple of decades. However, it object-orientation was incorporated into it at a later stage.

Defining Classes

The defclass macro allows creating user-defined classes. It establishes a class as a data type. It has the following syntax −

(defclass class-name (superclass-name*)
   (slot-description*)
   class-option*))

The slots are variables that store data, or fields.

A slot-description has the form (slot-name slot-option*), where each option is a keyword followed by a name, expression and other options. Most commonly used slot options are −

  • :accessor function-name

  • :initform expression

  • :initarg symbol

For example, let us define a Box class, with three slots length, breadth, and height.

(defclass Box () 
   (length 
   breadth 
   height)
)

Providing Access and Read/Write Control to a Slot

Unless the slots have values that can be accessed, read or written to, classes are pretty useless.

You can specify accessors for each slot when you define a class. For example, take our Box class −

(defclass Box ()
   ((length :accessor length)
      (breadth :accessor breadth)
      (height :accessor height)
   )
)

You can also specify separate accessor names for reading and writing a slot.

(defclass Box ()
   ((length :reader get-length :writer set-length)
      (breadth :reader get-breadth :writer set-breadth)
      (height :reader get-height :writer set-height)
   )
)

Creating Instance of a Class

The generic function make-instance creates and returns a new instance of a class.

It has the following syntax −

(make-instance class {initarg value}*)

Example

Let us create a Box class, with three slots, length, breadth and height. We will use three slot accessors to set the values in these fields.

Create a new source code file named main.lisp and type the following code in it.

(defclass box ()
   ((length :accessor box-length)
      (breadth :accessor box-breadth)
      (height :accessor box-height)
   )
)
(setf item (make-instance 'box))
(setf (box-length item) 10)
(setf (box-breadth item) 10)
(setf (box-height item) 5)
(format t "Length of the Box is ~d~%" (box-length item))
(format t "Breadth of the Box is ~d~%" (box-breadth item))
(format t "Height of the Box is ~d~%" (box-height item))

Когда вы выполняете код, он возвращает следующий результат -

Length of the Box is 10
Breadth of the Box is 10
Height of the Box is 5

Определение метода класса

В defmethodмакрос позволяет вам определить метод внутри класса. В следующем примере наш класс Box расширяется за счет включения метода с именем volume.

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(defclass box ()
   ((length :accessor box-length)
      (breadth :accessor box-breadth)
      (height :accessor box-height)
      (volume :reader volume)
   )
)

; method calculating volume   

(defmethod volume ((object box))
   (* (box-length object) (box-breadth object)(box-height object))
)

 ;setting the values 

(setf item (make-instance 'box))
(setf (box-length item) 10)
(setf (box-breadth item) 10)
(setf (box-height item) 5)

; displaying values

(format t "Length of the Box is ~d~%" (box-length item))
(format t "Breadth of the Box is ~d~%" (box-breadth item))
(format t "Height of the Box is ~d~%" (box-height item))
(format t "Volume of the Box is ~d~%" (volume item))

Когда вы выполняете код, он возвращает следующий результат -

Length of the Box is 10
Breadth of the Box is 10
Height of the Box is 5
Volume of the Box is 500

Наследование

LISP позволяет вам определять объект в терминах другого объекта. Это называетсяinheritance.Вы можете создать производный класс, добавив новые или другие функции. Производный класс наследует функции родительского класса.

Следующий пример объясняет это -

пример

Создайте новый файл исходного кода с именем main.lisp и введите в него следующий код.

(defclass box ()
   ((length :accessor box-length)
      (breadth :accessor box-breadth)
      (height :accessor box-height)
      (volume :reader volume)
   )
)

; method calculating volume   
(defmethod volume ((object box))
   (* (box-length object) (box-breadth object)(box-height object))
)
  
;wooden-box class inherits the box class  
(defclass wooden-box (box)
((price :accessor box-price)))

;setting the values 
(setf item (make-instance 'wooden-box))
(setf (box-length item) 10)
(setf (box-breadth item) 10)
(setf (box-height item) 5)
(setf (box-price item) 1000)

; displaying values
(format t "Length of the Wooden Box is ~d~%" (box-length item))
(format t "Breadth of the Wooden Box is ~d~%" (box-breadth item))
(format t "Height of the Wooden Box is ~d~%" (box-height item))
(format t "Volume of the Wooden Box is ~d~%" (volume item))
(format t "Price of the Wooden Box is ~d~%" (box-price item))

Когда вы выполняете код, он возвращает следующий результат -

Length of the Wooden Box is 10
Breadth of the Wooden Box is 10
Height of the Wooden Box is 5
Volume of the Wooden Box is 500
Price of the Wooden Box is 1000