Common Lisp ( CL ) — диалект языка программирования Lisp , опубликованный в стандартном документе Американского национального института стандартов (ANSI) ANSI INCITS 226-1994 (S2018) [1] (ранее X3.226-1994 (R1999) ). [2] Common Lisp HyperSpec , версия HTML с гиперссылками , была получена из стандарта ANSI Common Lisp. [3]
Язык Common Lisp был разработан как стандартизированный и улучшенный преемник Maclisp . К началу 1980-х годов несколько групп уже работали над различными преемниками MacLisp: Lisp Machine Lisp (он же ZetaLisp), Spice Lisp , NIL и S-1 Lisp . Common Lisp стремился унифицировать, стандартизировать и расширить возможности этих диалектов MacLisp. Common Lisp — это не реализация, а скорее спецификация языка . [4] Доступно несколько реализаций стандарта Common Lisp, включая бесплатное программное обеспечение и программное обеспечение с открытым исходным кодом, а также проприетарные продукты. [5] Common Lisp — это многопарадигмальный язык программирования общего назначения . Он поддерживает комбинацию процедурных , функциональных и объектно-ориентированных парадигм программирования. Являясь динамическим языком программирования , он облегчает эволюционную и поэтапную разработку программного обеспечения с итеративной компиляцией в эффективные программы времени выполнения. Эта поэтапная разработка часто выполняется в интерактивном режиме, не прерывая работающее приложение.
Он также поддерживает необязательные аннотации типов и приведение типов, которые при необходимости можно добавить на более поздних этапах профилирования и оптимизации, чтобы позволить компилятору генерировать более эффективный код. Например, fixnum
может содержать неупакованное целое число в диапазоне, поддерживаемом аппаратным обеспечением и реализацией, что обеспечивает более эффективную арифметику, чем для больших целых чисел или типов произвольной точности. Аналогично, с помощью объявлений оптимизации можно указать компилятору для каждого модуля или для каждой функции, какой тип уровня безопасности требуется .
Common Lisp включает CLOS — объектную систему , поддерживающую мультиметоды и комбинации методов. Это часто реализуется с помощью метаобъектного протокола.
Common Lisp расширяется с помощью стандартных функций, таких как макросы Lisp (преобразования кода) и макросы чтения (анализаторы ввода символов).
Common Lisp обеспечивает частичную обратную совместимость с Maclisp и оригинальным Lisp Джона Маккарти . Это позволяет портировать старое программное обеспечение Lisp на Common Lisp. [6]
Работа над Common Lisp началась в 1981 году после инициативы менеджера ARPA Боба Энгельмора по разработке единого стандартного диалекта Lisp. [7] Большая часть первоначального языкового дизайна была сделана через электронную почту. [8] [9] В 1982 году Гай Л. Стил-младший сделал первый обзор Common Lisp на симпозиуме ACM 1982 года по LISP и функциональному программированию. [10]
Документация по первому языку была опубликована в 1984 году под названием Common Lisp the Language (известная как CLtL1), первое издание. Второе издание (известное как CLtL2), опубликованное в 1990 году, включило в язык множество изменений, внесенных в процессе стандартизации ANSI Common Lisp: расширенный синтаксис LOOP, объектную систему Common Lisp, систему условий для обработки ошибок, интерфейс для красивый принтер и многое другое. Но CLtL2 не описывает окончательную версию стандарта ANSI Common Lisp и, следовательно, не является документацией ANSI Common Lisp. Окончательный стандарт ANSI Common Lisp был опубликован в 1994 году. С тех пор никаких обновлений стандарта не публиковалось. Различные расширения и улучшения Common Lisp (примеры — Unicode, Concurrency, ввод-вывод на основе CLOS) были предоставлены реализациями и библиотеками .
Common Lisp — это диалект Лиспа. Он использует S-выражения для обозначения как кода, так и структуры данных. Вызовы функций, макроформы и специальные формы записываются в виде списков, начиная с имени оператора, как в этих примерах:
( + 2 2 ) ; складывает 2 и 2, получая 4. Имя функции — «+». В Лиспе нет операторов как таковых.
( defvar *x* ) ; Гарантирует, что переменная *x* существует, ; не придавая ему значения. Звездочки являются частью ; имя, по соглашению обозначающее специальную (глобальную) переменную. ; Символ *x* также наделен свойством ; последующие его привязки являются скорее динамическими, чем лексическими. ( setf *x* 42.1 ) ; Устанавливает переменную *x* в значение с плавающей запятой 42,1.
;; Определите функцию, которая возводит число в квадрат: ( defun Square ( x ) ( * x x ))
;; Выполните функцию: ( квадрат 3 ) ; Возвращает 9
;; Конструкция let создает область для локальных переменных. Здесь ;; переменная «a» привязана к 6, а переменная «b» привязана ;; до 4. Внутри «let» находится «тело», куда возвращается последнее вычисленное значение. ;; Здесь результат сложения a и b возвращается из выражения let. ;; Переменные a и b имеют лексическую область видимости, если только символы не были ;; помечены как специальные переменные (например, предшествующим DEFVAR). ( пусть (( а 6 ) ( б 4 )) ( + а б )) ; возвращает 10
Common Lisp имеет множество типов данных .
Типы чисел включают целые числа , отношения , числа с плавающей запятой и комплексные числа . [11] Common Lisp использует большие числа для представления числовых значений произвольного размера и точности. Тип отношения точно представляет дроби — возможность, недоступная во многих языках. Common Lisp автоматически подбирает числовые значения между этими типами по мере необходимости.
Тип символов Common Lisp не ограничивается символами ASCII . Большинство современных реализаций допускают символы Юникода . [12]
Тип символа является общим для языков Лисп, но практически неизвестен за его пределами . Символ — это уникальный именованный объект данных, состоящий из нескольких частей: имени, значения, функции, списка свойств и пакета. Из них наиболее важными являются ячейка значения и ячейка функции . Символы в Лиспе часто используются аналогично идентификаторам в других языках: для хранения значения переменной; однако есть много других применений. Обычно при оценке символа возвращается его значение. Некоторые символы оцениваются сами по себе, например, все символы в пакете ключевых слов являются самооценочными. Логические значения в Common Lisp представлены символами самовычисления T и NIL. В Common Lisp есть пространства имен для символов, называемые «пакетами».
Доступен ряд функций для округления скалярных числовых значений различными способами. Функция round
округляет аргумент до ближайшего целого числа, а промежуточные значения округляются до четного целого числа. Функции truncate
, floor
и ceiling
округляют в сторону нуля, вниз или вверх соответственно. Все эти функции возвращают отброшенную дробную часть как вторичное значение. Например, (floor -2.5)
дает −3, 0,5; (ceiling -2.5)
дает -2, -0,5; (round 2.5)
дает 2, 0,5; и (round 3.5)
дает 4, -0,5.
Типы последовательностей в Common Lisp включают списки, векторы, битовые векторы и строки. Существует множество операций, которые могут работать с любым типом последовательности.
Как и почти во всех других диалектах Lisp, списки в Common Lisp состоят из conses , иногда называемых cons-ячейками или парами . Cons — это структура данных с двумя слотами, называемая car и cdr . Список — это связанная цепочка аргументов или пустой список. Автомобиль каждого преступника относится к члену списка (возможно, другого списка). Cdr каждого минуса относится к следующему минусу, за исключением последнего минуса в списке, чей cdr относится к значению nil
. Conses также можно легко использовать для реализации деревьев и других сложных структур данных; хотя обычно вместо этого рекомендуется использовать экземпляры структур или классов. Также возможно создавать циклические структуры данных с минусами.
Common Lisp поддерживает многомерные массивы и при необходимости может динамически изменять размер регулируемых массивов. Многомерные массивы можно использовать для матричной математики. Вектор — это одномерный массив. Массивы могут содержать элементы любого типа (даже смешанные типы в одном массиве) или могут быть специализированы для содержания элементов определенного типа, как в векторе битов. Обычно поддерживаются только несколько типов. Многие реализации могут оптимизировать функции массива, если используемый массив специализирован по типам. Стандартными являются два типа массивов, специализирующихся на типах: строка представляет собой вектор символов, а битовый вектор — это вектор битов .
Хэш-таблицы хранят связи между объектами данных. Любой объект может использоваться в качестве ключа или значения. Размер хеш-таблиц автоматически изменяется по мере необходимости.
Пакеты — это наборы символов, используемые в основном для разделения частей программы на пространства имен . Пакет может экспортировать некоторые символы, отмечая их как часть общедоступного интерфейса. Пакеты могут использовать другие пакеты.
Структуры , аналогичные структурам C и записям Pascal , представляют собой произвольные сложные структуры данных с любым количеством и типом полей (называемые слотами ). Структуры допускают одиночное наследование.
Классы похожи на структуры, но предлагают больше динамических функций и множественное наследование. (См. КЛОС ). Классы были добавлены в Common Lisp поздно, и существует некоторое концептуальное совпадение со структурами. Объекты, созданные из классов, называются экземплярами . Особый случай — универсальные функции. Универсальные функции — это одновременно функции и экземпляры.
Common Lisp поддерживает первоклассные функции . Например, можно писать функции, которые принимают другие функции в качестве аргументов или возвращают функции. Это позволяет описывать очень общие операции.
Библиотека Common Lisp в значительной степени полагается на такие функции высшего порядка. Например, sort
функция принимает оператор отношения в качестве аргумента и ключевую функцию в качестве необязательного ключевого аргумента. Это можно использовать не только для сортировки данных любого типа, но и для сортировки структур данных по ключу.
;; Сортирует список, используя функции > и < в качестве оператора отношения. ( сортировка ( список 5 2 6 3 1 4 ) #' > ) ; Возвращает (6 5 4 3 2 1) ( sort ( list 5 2 6 3 1 4 ) #' < ) ; Возврат (1 2 3 4 5 6)
;; Сортирует список по первому элементу каждого подсписка. ( sort ( list ' ( 9A ) ' ( 3B ) ' ( 4C ) ) #' < : key # ' first ) ; Возвращает ((3 Б) (4 С) (9 А))
Модель оценки функций очень проста. Когда оценщик встречает форму (f a1 a2...)
, он предполагает, что символ с именем f является одним из следующих:
lambda
.Если f — имя функции, то аргументы a1, a2, ..., an оцениваются в порядке слева направо, а функция находится и вызывается с этими значениями, предоставленными в качестве параметров.
Макрос определяет функцииdefun
, где определение функции дает имя функции, имена всех аргументов и тело функции:
( квадрат defun ( x ) ( * x x ))
Определения функций могут включать директивы компилятора , известные как объявления , которые предоставляют подсказки компилятору о настройках оптимизации или типах данных аргументов. Они также могут включать строки документации (docstrings), которые система Lisp может использовать для предоставления интерактивной документации:
( defun Square ( x ) "Вычисляет квадрат одинарного числа с плавающей запятой x." ( объявление ( одинарное число с плавающей запятой x ) ( оптимизация ( скорость 3 ) ( отладка 0 ) ( безопасность 1 ))) ( одинарное число с плавающей запятой ( * x Икс )))
Анонимные функции ( функциональные литералы ) определяются с помощью lambda
выражений, например, (lambda (x) (* x x))
для функции, которая возводит в квадрат свой аргумент. В стиле программирования на Лиспе часто используются функции высшего порядка, для которых полезно предоставлять анонимные функции в качестве аргументов.
Локальные функции могут быть определены с помощью flet
и labels
.
( флет (( квадрат ( x ) ( * x x ))) ( квадрат 3 ))
Существует несколько других операторов, связанных с определением функций и манипулированием ими. Например, функция может быть скомпилирована с compile
оператором. (Некоторые системы Lisp по умолчанию запускают функции с использованием интерпретатора, если не указано иное, другие компилируют каждую функцию).
Макрос defgeneric
определяет общие функции . Универсальные функции представляют собой набор методов . Макрос defmethod
определяет методы.
Методы могут специализировать свои параметры по стандартным классам CLOS , системным классам , классам структур или отдельным объектам. Для многих типов существуют соответствующие системные классы .
Когда вызывается универсальная функция, множественная диспетчеризация определяет эффективный метод, который следует использовать.
( defgeneric add ( a b ))
( defmethod add (( число ) ( число b ) ) ( + a b ))
( defmethod add (( вектор ) ( число b )) ( вектор карты ( лямбда ( n ) ( + n b ) ) a ))
( defmethod add (( a вектор ) ( b вектор )) ( карта 'вектор #' + a b ))
( defmethod add (( строка a ) ( строка b )) ( объединить ' строку a b ))
( добавить 2 3 ) ; возвращает 5 ( добавить #( 1 2 3 4 ) 7 ) ; возвращает #(8 9 10 11) ( добавьте #( 1 2 3 4 ) #( 4 3 2 1 ) ) ; возвращает #(5 5 5 5) ( добавить "COMMON" "LISP" ) ; возвращает «ОБЩИЙ ЛИСП»
Универсальные функции также являются первоклассным типом данных . У универсальных функций и методов гораздо больше возможностей, чем описано выше.
Пространство имен для имен функций отделено от пространства имен для переменных данных. Это ключевое различие между Common Lisp и Scheme . В Common Lisp операторы, определяющие имена в пространстве имен функции, включают defun
, flet
, и .labels
defmethod
defgeneric
Чтобы передать функцию по имени в качестве аргумента другой функции, необходимо использовать function
специальный оператор, обычно сокращаемый как #'
. Первый sort
пример выше относится к функции, названной символом >
в пространстве имен функции с кодом #'>
. И наоборот, чтобы вызвать функцию, переданную таким образом, можно было бы использовать оператор funcall
для аргумента.
Модель оценки Scheme проще: существует только одно пространство имен, и оцениваются все позиции в форме (в любом порядке), а не только аргументы. Поэтому код, написанный на одном диалекте, иногда сбивает с толку программистов, более опытных в другом. Например, многие программисты Common Lisp любят использовать описательные имена переменных, такие как список или строка , что может вызвать проблемы в Scheme, поскольку они локально затеняют имена функций.
Является ли отдельное пространство имен для функций преимуществом, является источником разногласий в сообществе Lisp. Обычно это называют дебатами о Lisp-1 и Lisp-2 . Lisp-1 относится к модели Scheme, а Lisp-2 относится к модели Common Lisp. Эти названия были придуманы в статье 1988 года Ричарда П. Габриэля и Кента Питмана , в которой два подхода подробно сравниваются. [13]
Common Lisp поддерживает концепцию множественных значений [14] , где любое выражение всегда имеет одно первичное значение , но оно также может иметь любое количество вторичных значений , которые могут быть получены и проверены заинтересованными вызывающими объектами. Эта концепция отличается от возврата значения списка, поскольку вторичные значения полностью необязательны и передаются через выделенный побочный канал. Это означает, что вызывающие объекты могут совершенно не подозревать о наличии вторичных значений, если они им не нужны, и это делает удобным использование механизма передачи информации, который иногда полезен, но не всегда необходим. Например,
TRUNCATE
[ 15] округляет заданное число до целого числа в сторону нуля. Однако он также возвращает остаток в качестве вторичного значения, что позволяет легко определить, какое значение было усечено. Он также поддерживает необязательный параметр делителя, который можно использовать для тривиального выполнения евклидова деления :( let (( x 1266778 ) ( y 458 )) ( привязка нескольких значений ( остаток от частного ) ( усечение x y ) ( формат nil "~A разделенный на ~A равен ~A остатку ~A" x y остаток от частного ) ) ) ;;;; => "1266778 разделить на 458 равно 2765, остаток 408"
GETHASH
[16] возвращает значение ключа в ассоциативной карте или в противном случае значение по умолчанию, а также вторичное логическое значение, указывающее, было ли найдено значение. Таким образом, код, который не заботится о том, было ли значение найдено или предоставлено по умолчанию, может просто использовать его как есть, но когда такое различие важно, он может проверить вторичное логическое значение и отреагировать соответствующим образом. Оба варианта использования поддерживаются одним и тем же вызовом, и ни один из них не является излишне обремененным или ограниченным другим. Наличие этой функции на уровне языка устраняет необходимость проверять существование ключа или сравнивать его с нулевым значением , как это было бы сделано в других языках.( defun get-ответ ( библиотека ) ( gethash ' библиотека ответов 42 )) ( defun the-answer-1 ( библиотека ) ( format nil "Ответ ~A" ( библиотека получения ответа ))) ;;;; Возвращает «Ответ: 42», если ОТВЕТ отсутствует в БИБЛИОТЕКЕ. ( defun the-ответ-2 ( библиотека ) ( привязка нескольких значений ( ответ уверен-р ) ( библиотека получения ответа ) ( если ( не уверен-р ) «Я не знаю» ( формат ноль «Ответ ~Ответ ) ))) ;;;; Возвращает «Я не знаю», если ОТВЕТ отсутствует в БИБЛИОТЕКЕ.
Множественные значения поддерживаются несколькими стандартными формами, наиболее распространенными из которых являются MULTIPLE-VALUE-BIND
специальные формы для доступа к вторичным значениям и VALUES
возврата нескольких значений:
( defun Magic-eight-ball () "Вернуть прогноз прогноза с вероятностью в качестве вторичного значения" ( значения "Перспектив хороший" ( random 1.0 ))) ;;;; => "Перспективы хорошие" ;;;; => 0,3187
Другие типы данных в Common Lisp включают:
Как и программы на многих других языках программирования, программы на Common Lisp используют имена для обозначения переменных, функций и многих других типов сущностей. Именованные ссылки зависят от области действия.
Связь между именем и объектом, на который это имя ссылается, называется привязкой.
Область действия относится к набору обстоятельств, при которых имя определяется как имеющее определенную привязку.
Обстоятельства, определяющие область действия в Common Lisp, включают в себя:
(go x)
означает передачу управления метке x
, тогда как (print x)
относится к переменной x
. Обе области x
могут быть активны в одной и той же области текста программы, поскольку метки тегов находятся в отдельном пространстве имен от имен переменных. Специальная форма или макроформа имеет полный контроль над значениями всех символов в своем синтаксисе. Например, в (defclass x (a b) ())
определении класса — (a b)
это список базовых классов, поэтому эти имена ищутся в пространстве имен классов и x
не являются ссылкой на существующую привязку, а являются именем производного нового класса. от a
и b
. Эти факты вытекают исключительно из семантики defclass
. Единственным общим фактом в этом выражении является то, что оно defclass
относится к привязке макроса; все остальное на усмотрение defclass
.x
заключена в конструкцию привязки, такую как letкоторая определяет привязку для x
, то ссылка находится в области, созданной этой привязкой.Чтобы понять, на что ссылается символ, программист Common Lisp должен знать, какой тип ссылки выражается, какую область видимости он использует, если это ссылка на переменную (динамическая или лексическая область действия), а также ситуацию во время выполнения: в какой среде разрешена ссылка, где в среду была введена привязка и т. д.
Некоторые среды Lisp являются глобальными. Например, если определен новый тип, он впоследствии будет известен повсюду. Ссылки на этот тип позволяют найти его в этой глобальной среде.
Одним из типов среды в Common Lisp является динамическая среда. Привязки, установленные в этой среде, имеют динамический размер, что означает, что привязка устанавливается в начале выполнения некоторой конструкции, например блока let
, и исчезает, когда эта конструкция завершает выполнение: ее время жизни привязано к динамической активации и деактивации Блок. Однако динамическая привязка не просто видна внутри этого блока; он также виден всем функциям, вызываемым из этого блока. Этот тип видимости известен как неопределенная область действия. Привязки, которые имеют динамическую протяженность (срок действия, связанный с активацией и деактивацией блока) и неопределенную область действия (видимую для всех функций, вызываемых из этого блока), называются динамическими.
Common Lisp поддерживает переменные с динамической областью действия, которые также называются специальными переменными. Некоторые другие виды привязок также обязательно имеют динамическую область действия, например перезапуски и теги catch. Привязки функций не могут быть динамически ограничены с помощью flet
(которые обеспечивают только привязки функций с лексической областью), но объекты функций (объект первого уровня в Common Lisp) могут быть назначены переменным с динамической областью, привязаны с помощью let
в динамической области, а затем вызваны с помощью funcall
или APPLY
.
Динамическая область чрезвычайно полезна, поскольку она добавляет ссылочную ясность и дисциплину к глобальным переменным . В информатике глобальные переменные не одобряются как потенциальные источники ошибок, поскольку они могут привести к появлению специальных, скрытых каналов связи между модулями, которые приводят к нежелательным, неожиданным взаимодействиям.
В Common Lisp специальная переменная, имеющая привязку только верхнего уровня, ведет себя точно так же, как глобальная переменная в других языках программирования. В него можно сохранить новое значение, и это значение просто заменяет то, что находится в привязке верхнего уровня. Неосторожная замена значения глобальной переменной лежит в основе ошибок, вызванных использованием глобальных переменных. Однако другой способ работы со специальной переменной — присвоить ей новую локальную привязку внутри выражения. Иногда это называют «перепривязкой» переменной. Привязка переменной с динамической областью временно создает новое место в памяти для этой переменной и связывает имя с этим местом. Пока эта привязка действует, все ссылки на эту переменную относятся к новой привязке; предыдущая привязка скрыта. Когда выполнение выражения привязки завершается, область временной памяти исчезает и открывается старая привязка с неповрежденным исходным значением. Конечно, несколько динамических привязок для одной и той же переменной могут быть вложенными.
В реализациях Common Lisp, поддерживающих многопоточность, динамические области действия специфичны для каждого потока выполнения. Таким образом, специальные переменные служат абстракцией для локального хранилища потоков. Если один поток перепривязывает специальную переменную, эта перепривязка не влияет на эту переменную в других потоках. Значение, хранящееся в привязке, может быть получено только потоком, создавшим эту привязку. Если каждый поток связывает какую-то специальную переменную *x*
, то он *x*
ведет себя как локальное хранилище потока. Среди потоков, которые не перепривязывают *x*
, он ведет себя как обычный глобальный: все эти потоки ссылаются на одну и ту же привязку верхнего уровня *x*
.
Динамические переменные можно использовать для расширения контекста выполнения дополнительной контекстной информацией, которая неявно передается от функции к функции без необходимости появления в качестве дополнительного параметра функции. Это особенно полезно, когда передача управления должна проходить через уровни несвязанного кода, который просто невозможно расширить дополнительными параметрами для передачи дополнительных данных. Подобная ситуация обычно требует использования глобальной переменной. Эту глобальную переменную необходимо сохранить и восстановить, чтобы схема не нарушалась при рекурсии: об этом позаботится перепривязка динамической переменной. И эта переменная должна быть локальной для потока (в противном случае необходимо использовать большой мьютекс), чтобы схема не нарушалась под потоками: реализации динамической области видимости также могут позаботиться об этом.
В библиотеке Common Lisp имеется множество стандартных специальных переменных. Например, все стандартные потоки ввода-вывода хранятся в привязках верхнего уровня известных специальных переменных. Стандартный поток вывода хранится в *standard-output*.
Предположим, функция foo записывает в стандартный вывод:
( defun foo () ( формат t "Привет, мир" ))
Чтобы записать вывод в виде символьной строки, *standard-output* можно привязать к строковому потоку и вызвать:
( с выводом в строку ( *стандартный вывод* ) ( foo ))
-> «Привет, мир» ; собранный вывод возвращается в виде строки
Common Lisp поддерживает лексические среды. Формально привязки в лексической среде имеют лексическую область действия и могут иметь либо неопределенный, либо динамический размер, в зависимости от типа пространства имен. Лексическая область видимости означает, что видимость физически ограничена блоком, в котором установлена привязка. Ссылки, которые не текстуально (т.е. лексически) не встроены в этот блок, просто не видят этой привязки.
Теги в TAGBODY имеют лексическую область видимости. Выражение (GO X) является ошибочным, если оно не встроено в TAGBODY, который содержит метку X. Однако привязки меток исчезают, когда TAGBODY завершает свое выполнение, поскольку они имеют динамический размер. Если этот блок кода вводится повторно путем вызова лексического замыкания , тело этого замыкания не может попытаться передать управление тегу через GO:
( defvar *спрятано* ) ;; будет содержать функцию ( tagbody ( setf *stashed* ( лямбда () ( go some-label ))) ( go end-label ) ;; пропустить (print "Hello") some-label ( print "Hello" ) end-label ) -> НОЛЬ
Когда TAGBODY выполняется, он сначала оценивает форму setf, которая сохраняет функцию в специальной переменной *stashed*. Затем (go end-label) передает управление конечной метке, пропуская код (выводит «Hello»). Поскольку конечная метка находится в конце тела тега, тело тега завершается, что дает NIL. Предположим, что ранее запомненная функция теперь вызывается:
( funcall *спрятано* ) ;; Ошибка!
Эта ситуация ошибочна. Ответом одной реализации является состояние ошибки, содержащее сообщение «GO: тело тега для тега SOME-LABEL уже оставлено». Функция попыталась вычислить (go some-label), которая лексически встроена в тело тега, и разрешилась в метку. Однако тело тега не выполняется (его экстент закончился), поэтому передача управления не может произойти.
Привязки локальных функций в Lisp имеют лексическую область видимости , а привязки переменных также имеют лексическую область видимости по умолчанию. В отличие от меток GO, обе они имеют неопределенную протяженность. Когда устанавливается лексическая функция или привязка переменной, эта привязка продолжает существовать до тех пор, пока возможны ссылки на нее, даже после того, как конструкция, установившая привязку, завершилась. Ссылки на лексические переменные и функции после завершения их устанавливающей конструкции возможны благодаря лексическим замыканиям .
Лексическое связывание — это режим связывания по умолчанию для переменных Common Lisp. Для отдельного символа его можно переключить в динамическую область действия либо с помощью локального объявления, либо с помощью глобального объявления. Последнее может произойти неявно за счет использования такой конструкции, как DEFVAR или DEFPARAMETER. В программировании на Common Lisp существует важное соглашение, согласно которому специальные переменные (то есть с динамической областью действия) имеют имена, которые начинаются и заканчиваются знаком звездочки, что *
называется « соглашением о наушниках ». [17] При соблюдении этого соглашения фактически создается отдельное пространство имен для специальных переменных, так что переменные, предназначенные для лексического использования, не могут случайно стать специальными.
Лексическая область видимости полезна по нескольким причинам.
Во-первых, ссылки на переменные и функции могут быть скомпилированы в эффективный машинный код, поскольку структура среды выполнения относительно проста. Во многих случаях его можно оптимизировать для стекового хранилища, поэтому открытие и закрытие лексических областей требует минимальных затрат. Даже в тех случаях, когда необходимо создать полные замыкания, доступ к среде замыкания по-прежнему эффективен; обычно каждая переменная становится смещением в векторе привязок, и поэтому ссылка на переменную становится простой инструкцией загрузки или сохранения с режимом адресации «база плюс смещение» .
Во-вторых, лексическая область видимости (в сочетании с неопределенной протяженностью) порождает лексическое замыкание , которое, в свою очередь, создает целую парадигму программирования, основанную на использовании функций, являющихся первоклассными объектами, что лежит в основе функционального программирования.
В-третьих, возможно, самое главное: даже если лексические замыкания не используются, использование лексической области видимости изолирует программные модули от нежелательных взаимодействий. Из-за ограниченной видимости лексические переменные являются частными. Если один модуль A связывает лексическую переменную X и вызывает другой модуль B, ссылки на X в B не будут случайно разрешаться в X, связанный в A. B просто не имеет доступа к X. Для ситуаций, в которых дисциплинированное взаимодействие через переменную недопустимо. желательно, чтобы Common Lisp предоставлял специальные переменные. Специальные переменные позволяют модулю A установить привязку к переменной X, которая видна другому модулю B, вызываемому из A. Возможность сделать это является преимуществом, а возможность предотвратить это также является преимуществом; следовательно, Common Lisp поддерживает как лексическую, так и динамическую область видимости .
Макрос в Лиспе внешне напоминает функцию. Однако вместо выражения, которое вычисляется, оно представляет собой преобразование исходного кода программы. Макрос получает исходный код, который он окружает, в качестве аргументов, привязывает его к своим параметрам и вычисляет новую исходную форму. Эта новая форма также может использовать макрос. Расширение макроса повторяется до тех пор, пока новая исходная форма не будет использовать макрос. Окончательная вычисленная форма — это исходный код, исполняемый во время выполнения.
Типичное использование макросов в Лиспе:
Различные стандартные функции Common Lisp также необходимо реализовать в виде макросов, например:
setf
абстракция, позволяющая настраиваемые расширения операторов присваивания/доступа во время компиляции.with-accessors
, и другие with-slots
подобные макросыwith-open-file
WITH
if
или cond
является макросом, построенным на другом специальном операторе; when
и unless
состоят из макросовloop
предметно-ориентированный языкМакросы определяются макросом defmacro . Специальный макролет оператора позволяет определять локальные макросы (с лексической областью действия). Также можно определить макросы для символов, используя define-symbol-macro иsymbol -macrolet .
Книга Пола Грэма «О Лиспе» подробно описывает использование макросов в Common Lisp. Книга Дуга Хойта Let Over Lambda расширяет обсуждение макросов, утверждая, что «Макросы — это единственное величайшее преимущество lisp как языка программирования и единственное величайшее преимущество любого языка программирования». Хойт приводит несколько примеров итеративной разработки макросов.
Макросы позволяют программистам Lisp создавать новые синтаксические формы на языке. Одним из типичных применений является создание новых структур управления. В примере макроса предусмотрена until
конструкция цикла. Синтаксис:
(до тестовой формы*)
Определение макроса до тех пор, пока :
( defmacro Until ( test &body body ) ( let (( start-tag ( gensym "START" )) ( end-tag ( gensym "END" ))) ` ( tagbody , start-tag ( When , test ( go , end- тег )) ( progn ,@ body ) ( go , начальный тег ) , конечный тег )))
tagbody — это примитивный специальный оператор Common Lisp, который предоставляет возможность называть теги и использовать форму go для перехода к этим тегам. Обратная кавычка ` предоставляет нотацию, которая предоставляет шаблоны кода, в которых заполняются значения форм, которым предшествует запятая. Формы, которым предшествует запятая и знак at, объединяются . Форма tagbody проверяет конечное условие. Если условие истинно, оно переходит к конечному тегу. В противном случае предоставленный основной код выполняется, а затем переходит к начальному тегу.
Пример использования приведенного выше макроса до :
( до ( = ( случайное 10 ) 0 ) ( строка записи «Привет» ))
Код можно расширить с помощью функции macroexpand-1 . Расширение приведенного выше примера выглядит следующим образом:
( TAGBODY #:START1136 ( WHEN ( ZEROP ( RANDOM 10 )) ( GO #:END1137 )) ( PROGN ( WRITE-LINE "hello" ) )) ( GO #:START1136 ) #:END1137 )
Во время раскрытия макроса значение переменной test равно (= (random 10) 0) , а значение тела переменной равно ( (write-line "Hello")) . Тело представляет собой список форм.
Символы обычно автоматически повышаются. В расширении используется TAGBODY с двумя метками. Символы для этих меток вычисляются GENSYM и не интернируются ни в один пакет. Две формы go используют эти теги для перехода. Поскольку tagbody — это примитивный оператор в Common Lisp (а не макрос), он не будет расширен во что-то еще. В развернутой форме используется макрос When , который также будет развернут. Полное раскрытие исходной формы называется обходом кода .
В полностью развернутой ( пройденной ) форме форма When заменяется примитивом if :
( TAGBODY #:START1136 ( IF ( ZEROP ( RANDOM 10 )) ( PROGN ( GO #:END1137 )) NIL ) ( PROGN ( WRITE-LINE "hello" )) ( GO #:START1136 )) #:END1137 )
Все макросы должны быть расширены, прежде чем содержащий их исходный код сможет быть нормально оценен или скомпилирован. Макросы можно считать функциями, которые принимают и возвращают S-выражения — аналогично абстрактным синтаксическим деревьям , но не ограничиваясь ими. Эти функции вызываются перед оценщиком или компилятором для создания окончательного исходного кода. Макросы написаны на обычном Common Lisp и могут использовать любой доступный оператор Common Lisp (или сторонний).
Макросы Common Lisp способны к тому, что обычно называют захватом переменных , когда символы в теле расширения макроса совпадают с символами в контексте вызова, что позволяет программисту создавать макросы, в которых различные символы имеют особое значение. Термин «захват переменных» несколько вводит в заблуждение, поскольку все пространства имен уязвимы для нежелательного захвата, включая пространство имен операторов и функций, пространство имен меток тега, тег catch, обработчик условий и пространства имен перезапуска.
Захват переменных может привести к дефектам программного обеспечения. Это происходит одним из следующих двух способов:
Диалект Scheme Lisp предоставляет систему написания макросов, которая обеспечивает ссылочную прозрачность, устраняющую оба типа проблем захвата. Этот тип макросистемы иногда называют «гигиеничным», особенно его сторонники (которые считают макросистемы, которые не решают эту проблему автоматически, негигиеничными). [ нужна цитата ]
В Common Lisp гигиена макросов обеспечивается одним из двух способов.
Один из подходов — использовать gensyms : гарантированно уникальные символы, которые можно использовать в макрорасширении без угрозы захвата. Использование gensyms в определении макроса — это рутинная работа, но можно написать макросы, которые упрощают создание и использование gensyms. Gensyms легко решает проблему захвата типа 2, но они не применимы к захвату типа 1 таким же образом, поскольку расширение макроса не может переименовывать мешающие символы в окружающем коде, которые захватывают его ссылки. Gensyms можно использовать для предоставления стабильных псевдонимов для глобальных символов, необходимых для макрорасширения. В расширении макроса будут использоваться эти секретные псевдонимы, а не общеизвестные имена, поэтому переопределение общеизвестных имен не окажет вредного воздействия на макрос.
Другой подход — использовать пакеты. Макрос, определенный в своем собственном пакете, может просто использовать внутренние символы этого пакета в своем расширении. Использование пакетов касается захвата типа 1 и типа 2.
Однако пакеты не решают проблему захвата ссылок типа 1 на стандартные функции и операторы Common Lisp. Причина в том, что использование пакетов для решения проблем захвата основано на использовании частных символов (символов в одном пакете, которые не импортируются или иным образом не становятся видимыми в других пакетах). Принимая во внимание, что символы библиотеки Common Lisp являются внешними и часто импортируются или становятся видимыми в определяемых пользователем пакетах.
Ниже приведен пример нежелательного захвата пространства имен оператора, происходящего при расширении макроса:
;; расширение UNTIL широко использует DO ( defmacro до тех пор, пока ( выражение &body body ) ` ( do () ( , выражение ) ,@ body )) ;; макролет устанавливает привязку лексического оператора для DO ( макролет (( do ( ... ) ... что-то еще ... )) ( до ( = ( случайный 10 ) 0 ) ( строка записи "Hello" )))
Макрос until
раскроется в форму, вызов которой do
предназначен для ссылки на стандартный макрос Common Lisp do
. Однако в данном контексте do
это может иметь совершенно другое значение и until
работать некорректно.
Common Lisp решает проблему сокрытия стандартных операторов и функций, запрещая их переопределение. Поскольку он переопределяет стандартный оператор do
, предыдущий на самом деле является фрагментом несоответствующего Common Lisp, который позволяет реализациям диагностировать и отклонять его.
Система условий отвечает за обработку исключений в Common Lisp. [18] Он предоставляет условия , обработчики и перезапуск . Условия — это объекты, описывающие исключительную ситуацию (например, ошибку). Если условие сигнализируется, система Common Lisp ищет обработчик для этого типа условия и вызывает обработчик. Теперь обработчик может искать перезапуски и использовать один из этих перезапусков для автоматического устранения текущей проблемы, используя такую информацию, как тип условия и любую соответствующую информацию , предоставленную как часть объекта условия, и вызывать соответствующую функцию перезапуска.
Эти перезапуски, если они не обрабатываются кодом, могут быть представлены пользователям (как часть пользовательского интерфейса, например, отладчика), чтобы пользователь мог выбрать и вызвать один из доступных перезапусков. Поскольку обработчик условия вызывается в контексте ошибки (без раскручивания стека), полное восстановление ошибки возможно во многих случаях, когда другие системы обработки исключений уже завершили бы текущую подпрограмму. Сам отладчик также можно настроить или заменить с помощью *debugger-hook*
динамической переменной. Код, найденный в формах unwind-protect, таких как финализаторы, также будет выполнен соответствующим образом, несмотря на исключение.
В следующем примере (с использованием символики Genera ) пользователь пытается открыть файл в функциональном тесте Lisp , вызываемом из Read-Eval-Print-LOOP ( REPL ), когда файл не существует. Система Lisp обеспечивает четыре перезапуска. Пользователь выбирает команду «Повторить OPEN», используя другой путь , и вводит другой путь (lispm-init.lisp вместо lispm-int.lisp). Код пользователя не содержит кода обработки ошибок. Весь код обработки ошибок и перезапуска обеспечивается системой Lisp, которая может обрабатывать и исправлять ошибку, не завершая работу пользовательского кода.
Команда: (тест «>zippy>lispm-int.lisp»)Ошибка: Файл не найден. Для lispm:>zippy>lispm-int.lisp.newestЛМФС:ОТКРЫТЫЙ-ЛОКАЛЬНЫЙ-LMFS-1 Аргумент 0: #P"lispm:>zippy>lispm-int.lisp.newest"sA, <Resume>: повторите попытку OPEN lispm:>zippy>lispm-int.lisp.newestSB: Повторить попытку OPEN, используя другой путь.sC, <Abort>: возврат на верхний уровень Lisp на сервере TELNET.SD: перезапустить процесс терминала TELNET.-> Повторить попытку OPEN, используя другой путь.Вместо этого используйте путь [default lispm:>zippy>lispm-int.lisp.newest]: lispm:>zippy>lispm-init.lisp.newest...программа продолжается
Common Lisp включает в себя набор инструментов для объектно-ориентированного программирования , Common Lisp Object System или CLOS . Питер Норвиг объясняет, насколько проще реализовать многие шаблоны проектирования на динамическом языке с функциями CLOS (множественное наследование, миксины, мультиметоды, метаклассы, комбинации методов и т. д.). [19] Было предложено включить несколько расширений Common Lisp для объектно-ориентированного программирования в стандарт ANSI Common Lisp, но в конечном итоге CLOS была принята в качестве стандартной объектной системы для Common Lisp. CLOS — это динамическая объектная система с множественной диспетчеризацией и множественным наследованием , которая радикально отличается от средств ООП, имеющихся в статических языках, таких как C++ или Java . Будучи динамической объектной системой, CLOS позволяет во время выполнения вносить изменения в общие функции и классы. Методы можно добавлять и удалять, классы можно добавлять и переопределять, объекты можно обновлять в соответствии с изменениями классов, а класс объектов можно изменять.
CLOS интегрирован в ANSI Common Lisp. Универсальные функции могут использоваться как обычные функции и представляют собой первоклассный тип данных. Каждый класс CLOS интегрирован в систему типов Common Lisp. Многие типы Common Lisp имеют соответствующий класс. Существует больше возможностей использования CLOS для Common Lisp. В спецификации не сказано, реализуются ли условия с помощью CLOS. Пути и потоки могут быть реализованы с помощью CLOS. Эти дополнительные возможности использования CLOS для ANSI Common Lisp не являются частью стандарта. Фактические реализации Common Lisp используют CLOS для имен путей, потоков, ввода-вывода, условий, реализации самого CLOS и многого другого.
Интерпретатор Lisp напрямую выполняет исходный код Lisp, предоставленный в виде объектов Lisp (списки, символы, числа и т. д.), считанных из s-выражений. Компилятор Lisp генерирует байт-код или машинный код из исходного кода Lisp. Common Lisp позволяет компилировать как отдельные функции Lisp в памяти, так и компилировать целые файлы во внешний хранимый скомпилированный код ( fasl- файлы).
Несколько реализаций более ранних диалектов Лиспа содержали как интерпретатор, так и компилятор. К сожалению, часто семантика была разной. Эти более ранние Lisps реализовали лексическую область видимости в компиляторе и динамическую область видимости в интерпретаторе. Common Lisp требует, чтобы и интерпретатор, и компилятор по умолчанию использовали лексическую область видимости. Стандарт Common Lisp описывает как семантику интерпретатора, так и компилятора. Компилятор можно вызвать с помощью функции compile для отдельных функций и с помощью функции compile-file для файлов. Common Lisp позволяет объявлять типы и предоставляет способы влиять на политику генерации кода компилятора. Для последнего различным качествам оптимизации могут быть присвоены значения от 0 (не важно) до 3 (наиболее важно): скорость , пространство , безопасность , отладка и скорость компиляции .
Существует также функция для оценки кода Lisp: eval
. eval
принимает код в виде предварительно проанализированных s-выражений, а не в виде текстовых строк, как в некоторых других языках. Таким образом, код может быть создан с помощью обычных функций Lisp для создания списков и символов, а затем этот код может быть вычислен с помощью функции eval
. Некоторые реализации Common Lisp (например, Clozure CL и SBCL) реализуются eval
с использованием своего компилятора. Таким образом код компилируется, даже если он оценивается с помощью функции eval
.
Компилятор файлов вызывается с помощью функции compile-file . Сгенерированный файл с скомпилированным кодом называется файлом fasl (от fast load ). Эти fasl- файлы, а также файлы исходного кода можно загрузить с помощью функции load в работающую систему Common Lisp. В зависимости от реализации компилятор файла генерирует байт-код (например, для виртуальной машины Java ), код языка C (который затем компилируется компилятором C) или, напрямую, собственный код.
Реализации Common Lisp можно использовать в интерактивном режиме, даже если код полностью скомпилирован. Таким образом, идея интерпретируемого языка неприменима к интерактивному Common Lisp.
Язык проводит различие между временем чтения, временем компиляции, временем загрузки и временем выполнения и позволяет пользовательскому коду также проводить это различие для выполнения желаемого типа обработки на нужном этапе.
Некоторые специальные операторы предназначены специально для интерактивной разработки; например, defvar
присвоит значение предоставленной переменной только в том случае, если она еще не была привязана, и defparameter
всегда будет выполнять присвоение. Это различие полезно при интерактивной оценке, компиляции и загрузке кода в живом образе.
Также предусмотрены некоторые функции, помогающие писать компиляторы и интерпретаторы. Символы состоят из объектов первого уровня и могут напрямую управляться пользовательским кодом. Специальный progv
оператор позволяет программно создавать лексические привязки, при этом пакетами также можно манипулировать. Компилятор Lisp доступен во время выполнения для компиляции файлов или отдельных функций. Это упрощает использование Lisp в качестве промежуточного компилятора или интерпретатора другого языка.
Следующая программа вычисляет наименьшее количество людей в комнате, для которых вероятность уникальных дней рождения меньше 50% ( парадокс дней рождения , где для 1 человека вероятность очевидно 100%, для 2 — 364/365 и т. д. ). Ответ: 23.
В Common Lisp константы по соглашению заключаются в символы +.
( defconstant +размер года+ 365 ) ( defun день рождения-парадокс ( вероятность количества людей ) ( let (( новая вероятность ( * ( / ( - +размер года+ количество людей ) +размер года+ ) вероятность ))) ( if ( < new -вероятность 0,5 ) ( 1+ количество человек ) ( парадокс дня рождения новая вероятность ( 1+ количество человек )))))
Вызов функции примера с использованием REPL (цикл чтения Eval Print):
CL-USER > (парадокс дня рождения 1.0 1)23
Мы определяем класс person
и метод для отображения имени и возраста человека. Далее мы определяем группу людей как список person
объектов. Затем мы перебираем отсортированный список.
( defclass person () (( name :initarg :name :accessor person-name ) ( age :initarg :age :accessor person-age )) ( :documentation "Класс PERSON со слотами NAME и AGE." )) ( defmethod display (( object person ) stream ) "Отображение объекта PERSON в выходной поток." ( with-slots ( name age ) object ( formatstream "~a (~a)" name age )) ) ( defparameter *group* ( list ( make-instance 'person :name "Bob" :age 33 ) ( make-instance 'person :name "Chris" :age 16 ) ( make-instance 'person :name "Ash" :age 23 )) «Список объектов PERSON». ) ( dolist ( person ( sort ( copy-list *group* ) #' > :key #' person-age )) ( display person *standard-output* ) ( terpri ))
Он печатает три имени по убыванию возраста.
Боб (33)Ясень (23)Крис (16)
Демонстрируется использование макроса LOOP:
( defun power ( x n ) ( цикл с результатом = 1 while ( plusp n ) if ( нечетный n ) do ( setf result ( * result x )) do ( setf x ( * x x ) n ( truncate n 2 )) наконец ( возврат результата )))
Пример использования:
CL-USER > ( мощность 2 200 ) 1606938044258990275541962092341162602522202993782792835301376
Сравните со встроенным возведением в степень:
CL-USER > ( = ( expt 2 200 ) ( power 2 200 )) T
With-OPEN-FILE — это макрос, который открывает файл и предоставляет поток. Когда форма возвращается, файл автоматически закрывается. FUNCALL вызывает объект функции. LOOP собирает все строки, соответствующие предикату.
( defun list-matching-lines ( предикат файла ) "Возвращает список строк в файле, для которых предикат, примененный к строке, возвращает T." ( with-open-file ( stream file ) ( цикл для строки = ( read- строковый поток nil nil ) while line When ( строка предиката funcall ) собирает ее )))
Функция AVAILABLE-SHELLS вызывает указанную выше функцию LIST-MATCHING-LINES с именем пути и анонимной функцией в качестве предиката. Предикат возвращает путь к оболочке или NIL (если строка не является именем файла оболочки).
( defun доступные оболочки ( &optional ( file #p"/etc/shells" )) ( файл списка совпадающих строк ( лямбда ( строка ) ( и ( plusp ( длина строки )) ( char= ( строка char 0 ) #\ / ) ( путь ( string-right-trim ' ( #\space #\tab ) строка ))))))
Пример результатов (в Mac OS X 10.6):
CL-USER > ( доступные оболочки ) ( #P"/bin/bash" #P"/bin/csh" #P"/bin/ksh" #P"/bin/sh" #P"/bin/tcsh" #P"/bin/zsh" )
Common Lisp чаще всего сравнивают и противопоставляют Scheme — хотя бы потому, что это два самых популярных диалекта Lisp. Scheme предшествовал CL и исходит не только из той же традиции Lisp, но и от одних и тех же инженеров — Гай Стил , вместе с которым Джеральд Джей Сассман разработал Scheme, возглавлял комитет по стандартизации Common Lisp.
Common Lisp — это язык программирования общего назначения, в отличие от вариантов Lisp, таких как Emacs Lisp и AutoLISP , которые представляют собой языки расширения, встроенные в определенные продукты (GNU Emacs и AutoCAD соответственно). В отличие от многих более ранних Lisp, Common Lisp (как и Scheme ) по умолчанию использует область лексических переменных как для интерпретируемого, так и для скомпилированного кода.
Большинство систем Lisp, чьи разработки способствовали созданию Common Lisp, например ZetaLisp и Franz Lisp, использовали переменные с динамической областью действия в своих интерпретаторах и переменные с лексической областью видимости в своих компиляторах. Scheme ввел в Lisp единоличное использование переменных с лексической областью; вдохновение от АЛГОЛА 68 . CL также поддерживает переменные с динамической областью действия, но они должны быть явно объявлены как «специальные». Между интерпретаторами и компиляторами ANSI CL нет различий в области видимости.
Common Lisp иногда называют Lisp-2 , а Scheme — Lisp-1 , имея в виду использование CL отдельных пространств имен для функций и переменных. (На самом деле, CL имеет множество пространств имен, например, для тегов go, имен блоков и loop
ключевых слов). Между сторонниками CL и Scheme существует давний спор по поводу компромиссов, связанных с использованием нескольких пространств имен. В Scheme (в широком смысле) необходимо избегать присвоения имен переменных, которые конфликтуют с функциями; Функции схемы часто имеют аргументы с именем lis
, lst
или lyst
, чтобы не конфликтовать с системной функцией list
. Однако в CL необходимо явно ссылаться на пространство имен функции при передаче функции в качестве аргумента, что также является частым явлением, как в примере sort
выше.
CL также отличается от Scheme обработкой логических значений. Схема использует специальные значения #t и #f для представления истины и ложности. CL следует старому соглашению Lisp об использовании символов T и NIL, причем NIL также обозначает пустой список. В CL любое значение, отличное от NIL, рассматривается как истинное с помощью условных операторов, таких как if
, тогда как в Scheme все значения, отличные от #f, рассматриваются как истинные. Эти соглашения позволяют некоторым операторам в обоих языках служить как предикатами (отвечая на логический вопрос), так и возвращая полезное значение для дальнейших вычислений, но в Scheme значение '(), которое эквивалентно NIL в Common Lisp, оценивается как true. в логическом выражении.
Наконец, документы стандартов Scheme требуют оптимизации хвостового вызова , чего нет в стандарте CL. Большинство реализаций CL предлагают оптимизацию хвостового вызова, хотя часто только тогда, когда программист использует директиву оптимизации. Тем не менее, общий стиль кодирования CL не благоприятствует повсеместному использованию рекурсии, которое предпочитает стиль Scheme — то, что программист Scheme выразил бы с помощью хвостовой рекурсии, пользователь CL обычно выразил бы с помощью итеративного выражения в , , do
или dolist
( loop
в последнее время) с помощью iterate
упаковка.
См. раздел Реализации категории Common Lisp .
Common Lisp определяется спецификацией (например, Ada и C ), а не одной реализацией (например, Perl ). Существует множество реализаций и областей стандартных деталей, в которых они могут существенно отличаться.
Кроме того, реализации, как правило, поставляются с расширениями, которые обеспечивают функциональность, не описанную в стандарте:
Бесплатные программные библиотеки с открытым исходным кодом были созданы для переносимой поддержки расширений Common Lisp и, в первую очередь, находятся в репозиториях Common-Lisp.net [20] и CLOCC (Common Lisp Open Code Collection) [21]. ] проекты.
Реализации Common Lisp могут использовать любое сочетание компиляции собственного кода, компиляции или интерпретации байт-кода. Common Lisp был разработан для поддержки инкрементальных , файловых и блочных компиляторов. Стандартные объявления для оптимизации компиляции (например, встраивание функций или специализация типов) предлагаются в спецификации языка. Большинство реализаций Common Lisp компилируют исходный код в машинный код . Некоторые реализации могут создавать (оптимизированные) автономные приложения. Другие компилируются в интерпретируемый байт-код , что менее эффективно, чем собственный код, но облегчает переносимость двоичного кода. Некоторые компиляторы компилируют код Common Lisp в код C. Заблуждение о том, что Lisp — это чисто интерпретируемый язык, скорее всего, связано с тем, что среды Lisp предоставляют интерактивную подсказку и этот код компилируется один за другим, поэтапно. В Common Lisp широко используется инкрементная компиляция.
Некоторые реализации на основе Unix ( CLISP , SBCL ) могут использоваться в качестве языка сценариев ; то есть вызывается системой прозрачно, как интерпретатор оболочки Perl или Unix . [22]
Common Lisp используется для разработки исследовательских приложений (часто в области искусственного интеллекта ), для быстрой разработки прототипов или для развернутых приложений.
Common Lisp используется во многих коммерческих приложениях, включая Yahoo! Сайт интернет-торговли Store, над созданием которого первоначально работал Пол Грэм , а затем был переписан на C++ и Perl . [47] Другие известные примеры включают:
Также существуют приложения с открытым исходным кодом, написанные на Common Lisp, такие как:
{{cite journal}}
: Требуется цитировать журнал |journal=
( помощь ){{cite journal}}
: Требуется цитировать журнал |journal=
( помощь )Хронологический список опубликованных (или готовых к публикации) книг о Common Lisp (языке) или о программировании с использованием Common Lisp (особенно о программировании искусственного интеллекта).