Общий Лисп - Common Lisp

  (Перенаправлено с Corman Common Lisp )

Common Lisp
Парадигма Мультипарадигма : процедурная , функциональная , объектно-ориентированная , мета , рефлексивная , общая
Семья Лисп
Разработано Скотт Фалман , Ричард П. Габриэль , Дэвид А. Мун , Кент Питман , Гай Стил , Дэн Вайнреб
Разработчик Комитет ANSI X3J13
Впервые появился 1984 (36 лет назад) , 1994 (26 лет назад) для ANSI Common Lisp ( 1984 ) ( 1994 )
Печатная дисциплина Динамичный , сильный
Объем Лексический, опционально динамический
Операционные системы Кроссплатформенность
Расширения имени файла .lisp, .lsp, .l, .cl, .fasl
Интернет сайт common-lisp .net
Основные реализации
Allegro CL , ABCL , CLISP , Clozure CL , CMUCL , ECL , GCL , LispWorks , Scieneer CL , SBCL , Symbolics Common Lisp
Диалекты
CLtL1, CLtL2, ANSI Common Lisp
Под влиянием
Лисп , Лисп-машина Лисп , Маклисп , Схема , Интерлисп
Под влиянием
Clojure , Дилан , Emacs Lisp , EuLisp , ISLISP , * Lisp , AutoLisp , Джулия , лосей , R , МАСТЕРСТВО , SubL

Common Lisp ( CL ) является диалектом языка программирования Lisp , опубликованный в ANSI стандарт документа ANSI INCITS 226-1994 (R2004) (ранее X3.226-1994 (R1999) ). Common Lisp HyperSpec , гиперссылка HTML версии, был получен из стандарта ANSI Common Lisp.

Язык Common Lisp был разработан как стандартизированный и улучшенный преемник Maclisp . К началу 1980-х несколько групп уже работали над разнообразными преемниками MacLisp: Lisp Machine Lisp (также известный как ZetaLisp), Spice Lisp , NIL и S-1 Lisp . Common Lisp стремился унифицировать, стандартизировать и расширить возможности этих диалектов MacLisp. Common Lisp - это не реализация, а спецификация языка . Доступно несколько реализаций стандарта Common Lisp, включая бесплатное программное обеспечение и программное обеспечение с открытым исходным кодом, а также проприетарные продукты. Common Lisp - это универсальный, многопарадигмальный язык программирования . Он поддерживает сочетание процедурной , функциональной и объектно-ориентированной парадигм программирования . Как язык динамического программирования , он облегчает эволюционную и инкрементную разработку программного обеспечения с итеративной компиляцией в эффективные исполняемые программы. Эта инкрементальная разработка часто выполняется в интерактивном режиме без прерывания работы приложения.

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

Common Lisp включает в себя CLOS , в систему объекта , которая поддерживает мультиметодов и комбинации методов. Часто это реализуется с помощью протокола метаобъектов .

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

Common Lisp обеспечивает частичную обратную совместимость с Maclisp и оригинальным Lisp Джона Маккарти . Это позволяет портировать старое программное обеспечение Lisp на Common Lisp.

История

Работа над Common Lisp началась в 1981 году после инициативы менеджера ARPA Боба Энгельмора по разработке единого стандартного диалекта Lisp сообщества. Большая часть первоначального языкового дизайна была сделана по электронной почте. В 1982 году Гай Л. Стил-младший сделал первый обзор Common Lisp на симпозиуме ACM 1982 года по LISP и функциональному программированию.

Документация на первом языке была опубликована в 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) были предоставлены реализациями и библиотеками (многие из которых доступны через Quicklisp ).

Синтаксис

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

 (+ 2 2)           ; adds 2 and 2, yielding 4. The function's name is '+'. Lisp has no operators as such.
 (defvar *x*)      ; Ensures that a variable *x* exists,
                   ; without giving it a value. The asterisks are part of
                   ; the name, by convention denoting a special (global) variable. 
                   ; The symbol *x* is also hereby endowed with the property that
                   ; subsequent bindings of it are dynamic, rather than lexical.
 (setf *x* 42.1)   ; Sets the variable *x* to the floating-point value 42.1
 ;; Define a function that squares a number:
 (defun square (x)
   (* x x))
 ;; Execute the function:
 (square 3)        ; Returns 9
 ;; The 'let' construct creates a scope for local variables. Here
 ;; the variable 'a' is bound to 6 and the variable 'b' is bound
 ;; to 4. Inside the 'let' is a 'body', where the last computed value is returned.
 ;; Here the result of adding a and b is returned from the 'let' expression.
 ;; The variables a and b have lexical scope, unless the symbols have been
 ;; marked as special variables (for instance by a prior DEFVAR).
 (let ((a 6)
       (b 4))
   (+ a b))        ; returns 10

Типы данных

Common Lisp имеет много типов данных .

Скалярные типы

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

Тип символов Common Lisp не ограничивается символами ASCII . Большинство современных реализаций позволяют использовать символы Unicode .

Тип символа является общим для языков Lisp, но в значительной степени неизвестен за пределами их. Символ - это уникальный именованный объект данных, состоящий из нескольких частей: имя, значение, функция, список свойств и пакет. Из них значение ячейки и функции клеток являются наиболее важными. Символы в Лиспе часто используются аналогично идентификаторам в других языках: для хранения значения переменной; однако есть много других применений. Обычно при оценке символа возвращается его значение. Некоторые символы оцениваются сами по себе, например, все символы в пакете ключевых слов являются самооценочными. Логические значения в 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 включают списки, векторы, битовые векторы и строки. Есть много операций, которые могут работать с любым типом последовательности.

Как и почти во всех других диалектах Лиспа, списки в Common Lisp состоят из conses , иногда называемых cons-ячейками или парами . Cons - это структура данных с двумя слотами, которые называются car и cdr . Список - это связанная цепочка заключений или пустой список. Автомобиль каждого минуса относится к члену списка (возможно, другого списка). Cdr каждого cons относится к следующему cons - за исключением последнего cons в списке, cdr которого относится к nilзначению. Conses также можно легко использовать для реализации деревьев и других сложных структур данных; хотя обычно рекомендуется использовать вместо этого экземпляры структуры или класса. Также возможно создавать круговые структуры данных с помощью conses.

Common Lisp поддерживает многомерные массивы и при необходимости может динамически изменять размер настраиваемых массивов. Для матричной математики можно использовать многомерные массивы. Вектор представляет собой одномерный массив. Массивы могут нести в качестве элементов любой тип (даже смешанные типы в одном массиве) или могут быть специализированы для хранения элементов определенного типа, как в векторе битов. Обычно поддерживаются только несколько типов. Многие реализации могут оптимизировать функции массива, когда используемый массив зависит от типа. Стандартными являются два типа специализированных массивов: строка - это вектор символов, а битовый вектор - это вектор бит .

Хеш-таблицы хранят ассоциации между объектами данных. Любой объект может использоваться как ключ или значение. Размер хеш-таблиц автоматически изменяется по мере необходимости.

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

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

Классы похожи на структуры, но предлагают более динамичные функции и множественное наследование. (См. ЗАКРЫТЬ ). Классы были добавлены в Common Lisp поздно, и есть некоторое концептуальное совпадение со структурами. Объекты, созданные из классов, называются экземплярами . Особый случай - универсальные функции. Общие функции - это как функции, так и экземпляры.

Функции

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

Библиотека Common Lisp сильно полагается на такие функции высшего порядка. Например, sortфункция принимает реляционный оператор в качестве аргумента и ключевую функцию в качестве необязательного аргумента ключевого слова. Это можно использовать не только для сортировки любого типа данных, но и для сортировки структур данных по ключу.

 ;; Sorts the list using the > and < function as the relational operator.
 (sort (list 5 2 6 3 1 4) #'>)   ; Returns (6 5 4 3 2 1)
 (sort (list 5 2 6 3 1 4) #'<)   ; Returns (1 2 3 4 5 6)
 ;; Sorts the list according to the first element of each sub-list.
 (sort (list '(9 A) '(3 B) '(4 C)) #'< :key #'first)   ; Returns ((3 B) (4 C) (9 A))

Модель оценки функций очень проста. Когда оценщик встречает форму, (f a1 a2...)он предполагает, что символ с именем f является одним из следующих:

  1. Специальный оператор (легко проверяется по фиксированному списку)
  2. Макрооператор (должен быть определен ранее)
  3. Имя функции (по умолчанию), которое может быть либо символом, либо подформой, начинающейся с символа lambda.

Если f - имя функции, то аргументы a1, a2, ..., an оцениваются в порядке слева направо, и функция обнаруживается и вызывается с этими значениями, указанными в качестве параметров.

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

Макросdefun определяет функцию , где определение функции задает имя функции, имен аргументов, а тело функции:

 (defun square (x)
   (* x x))

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

 (defun square (x)
   "Calculates the square of the single-float x."
   (declare (single-float x) (optimize (speed 3) (debug 0) (safety 1)))
   (the single-float (* x x)))

Анонимные функции ( функциональные литералы ) определяются с помощью lambdaвыражений, например, (lambda (x) (* x x))для функции, которая возводит в квадрат свой аргумент. Стиль программирования Lisp часто использует функции высшего порядка, для которых полезно предоставить анонимные функции в качестве аргументов.

Локальные функции могут быть определены с помощью fletи labels.

 (flet ((square (x)
          (* x x)))
   (square 3))

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

Определение общих функций и методов

Макрос defgenericопределяет общие функции . Универсальные функции - это набор методов . Макрос defmethodопределяет методы.

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

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

 (defgeneric add (a b))
 (defmethod add ((a number) (b number))
   (+ a b))
 (defmethod add ((a vector) (b number))
   (map 'vector (lambda (n) (+ n b)) a))
 (defmethod add ((a vector) (b vector))
   (map 'vector #'+ a b))
(defmethod add ((a string) (b string))
  (concatenate 'string a b))
 (add 2 3)                   ; returns 5
 (add #(1 2 3 4) 7)          ; returns #(8 9 10 11)
 (add #(1 2 3 4) #(4 3 2 1)) ; returns #(5 5 5 5)
 (add "COMMON " "LISP")      ; returns "COMMON LISP"

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

Пространство имен функции

Пространство имен для имен функций отделено от пространства имен для переменных данных. Это ключевое различие между Common Lisp и Scheme . Для Common Lisp, операторы , которые определяют имена в функции имен включают defun, flet, labels, defmethodи defgeneric.

Чтобы передать функцию по имени в качестве аргумента другой функции, необходимо использовать functionспециальный оператор, обычно обозначаемый как #'. Первый sortпример выше относится к функции, названной символом >в пространстве имен функции, с кодом #'>. И наоборот, чтобы вызвать переданную таким образом функцию, нужно использовать funcallоператор для аргумента.

Модель оценки схемы проще: существует только одно пространство имен, и все позиции в форме оцениваются (в любом порядке), а не только аргументы. Поэтому код, написанный на одном диалекте, иногда сбивает с толку программистов, более опытных в другом. Например, многие программисты на Common Lisp любят использовать описательные имена переменных, такие как список или строка, которые могут вызвать проблемы в Scheme, поскольку они будут локально затенять имена функций.

Вопрос о том, является ли отдельное пространство имен для функций преимуществом, является источником разногласий в сообществе Lisp. Обычно это называют дебатами Лисп-1 против Лисп-2 . Lisp-1 относится к модели Scheme, а Lisp-2 относится к модели Common Lisp. Эти названия были придуманы Ричардом П. Габриэлем и Кентом Питманом в статье 1988 года , в которой проводится подробное сравнение этих двух подходов.

Несколько возвращаемых значений

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

  • TRUNCATEФункция округляет заданное число до целого числа к нулю. Однако он также возвращает остаток в качестве вторичного значения, что позволяет очень легко определить, какое значение было усечено. Он также поддерживает необязательный параметр делителя, который можно использовать для тривиального выполнения евклидова деления :
(let ((x 1266778)
      (y 458))
  (multiple-value-bind (quotient remainder)
      (truncate x y)
    (format nil "~A divided by ~A is ~A remainder ~A" x y quotient remainder)))

;;;; => "1266778 divided by 458 is 2765 remainder 408"
  • GETHASHвозвращает значение ключа на ассоциативной карте или значение по умолчанию в противном случае и вторичное логическое значение, указывающее, было ли найдено значение. Таким образом, код, который не заботится о том, было ли значение найдено или предоставлено как значение по умолчанию, может просто использовать его как есть, но когда такое различие важно, он может проверить вторичное логическое значение и отреагировать соответствующим образом. Оба варианта использования поддерживаются одним и тем же вызовом, и ни один из них не обременен и не ограничен другим. Наличие этой функции на уровне языка устраняет необходимость проверять наличие ключа или сравнивать его с нулевым значением, как это было бы сделано на других языках.
(defun get-answer (library)
  (gethash 'answer library 42))

(defun the-answer-1 (library)
  (format nil "The answer is ~A" (get-answer library)))
;;;; Returns "The answer is 42" if ANSWER not present in LIBRARY

(defun the-answer-2 (library)
  (multiple-value-bind (answer sure-p)
      (get-answer library)
    (if (not sure-p)
        "I don't know"
     (format nil "The answer is ~A" answer))))
;;;; Returns "I don't know" if ANSWER not present in LIBRARY

Несколько значений поддерживаются несколькими стандартными формами, наиболее распространенными из которых являются MULTIPLE-VALUE-BINDспециальные формы для доступа к вторичным значениям и VALUESдля возврата нескольких значений:

(defun magic-eight-ball ()
  "Return an outlook prediction, with the probability as a secondary value"
  (values "Outlook good" (random 1.0)))

;;;; => "Outlook good"
;;;; => 0.3187

Другие типы

Другие типы данных в Common Lisp включают:

  • Имена путей представляют файлы и каталоги в файловой системе . Средство определения путей в Common Lisp является более общим, чем соглашения об именах файлов в большинстве операционных систем, что делает доступ программ на Lisp к файлам широко переносимым между различными системами.
  • Потоки ввода и вывода представляют собой источники и приемники двоичных или текстовых данных, такие как терминал или открытые файлы.
  • Common Lisp имеет встроенный генератор псевдослучайных чисел (PRNG). Объекты случайного состояния представляют собой многократно используемые источники псевдослучайных чисел, позволяя пользователю заполнить ГПСЧ или заставить его воспроизвести последовательность.
  • Условия - это тип, используемый для представления ошибок, исключений и других «интересных» событий, на которые программа может реагировать.
  • Классы являются объектами первого класса и сами являются экземплярами классов, называемых классами метаобъектов ( для краткости метаклассами ).
  • Таблицы чтения - это тип объекта, который контролирует, как средство чтения 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заключена в конструкцию привязки, такую ​​как a, letкоторая определяет привязку для x, то ссылка находится в области, созданной этой привязкой.
  • для ссылки на переменную, независимо от того, был ли символ переменной, локально или глобально, объявлен специальным. Это определяет, разрешается ли ссылка в лексической среде или в динамической среде.
  • конкретный экземпляр среды, в которой разрешена ссылка. Среда - это словарь времени выполнения, который сопоставляет символы привязкам. Каждый вид ссылки использует свою среду. Ссылки на лексические переменные разрешаются в лексической среде и так далее. С одной и той же ссылкой может быть связано более одной среды. Например, благодаря рекурсии или использованию нескольких потоков одновременно может существовать несколько активаций одной и той же функции. Эти активации используют один и тот же текст программы, но каждая имеет свой собственный экземпляр лексической среды.

Чтобы понять, к чему относится символ, программист на Common Lisp должен знать, какой тип ссылки выражается, какую область видимости она использует, если это ссылка на переменную (динамическая или лексическая область видимости), а также ситуацию во время выполнения: in какая среда является разрешенной ссылкой, где была введена привязка в среду и т. д.

Виды среды

Глобальный

Некоторые среды в Лиспе распространены глобально. Например, если новый тип определен, он будет известен везде после этого. Ссылки на этот тип ищите в этой глобальной среде.

Динамический

Одним из типов среды в Common Lisp является динамическая среда. Привязки, установленные в этой среде, имеют динамическую протяженность, что означает, что привязка устанавливается в начале выполнения некоторой конструкции, такой как letблок, и исчезает, когда эта конструкция завершает выполнение: ее время жизни привязано к динамической активации и деактивации Блок. Однако динамическая привязка не только видна внутри этого блока; он также виден всем функциям, вызываемым из этого блока. Этот тип видимости известен как неопределенная область видимости. Привязки, которые демонстрируют динамическую протяженность (время жизни, связанное с активацией и деактивацией блока) и неопределенную область действия (видимую для всех функций, вызываемых из этого блока), как говорят, имеют динамическую область видимости.

Common Lisp поддерживает переменные с динамической областью видимости, которые также называются специальными переменными. Некоторые другие виды привязок также обязательно имеют динамическую область видимости, например, перезапуск и теги перехвата. Привязки функций не могут иметь динамическую область видимости с помощью flet(что обеспечивает только привязки функций с лексической областью видимости), но объекты функций (объект первого уровня в Common Lisp) могут быть назначены переменным с letдинамической областью видимости, привязаны с помощью динамической области видимости, а затем вызваны с помощью funcallили APPLY.

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

В Common Lisp специальная переменная, имеющая привязку только верхнего уровня, ведет себя так же, как глобальная переменная в других языках программирования. В нем можно сохранить новое значение, и это значение просто заменяет то, что находится в привязке верхнего уровня. Неосторожная замена значения глобальной переменной лежит в основе ошибок, вызванных использованием глобальных переменных. Однако другой способ работы со специальной переменной - присвоить ей новую локальную привязку внутри выражения. Иногда это называют «повторным связыванием» переменной. Привязка переменной с динамической областью видимости временно создает новое место в памяти для этой переменной и связывает имя с этим расположением. Пока эта привязка действует, все ссылки на эту переменную относятся к новой привязке; предыдущая привязка скрыта. Когда выполнение выражения привязки завершается, временная ячейка памяти исчезает, и открывается старая привязка с неизменным исходным значением. Конечно, несколько динамических привязок для одной и той же переменной могут быть вложенными.

В реализациях Common Lisp, поддерживающих многопоточность, динамические области видимости специфичны для каждого потока выполнения. Таким образом, специальные переменные служат абстракцией для локального хранилища потоков. Если один поток повторно связывает специальную переменную, это повторное связывание не влияет на эту переменную в других потоках. Значение, хранящееся в привязке, может быть получено только потоком, создавшим эту привязку. Если каждый поток связывает какую-то специальную переменную *x*, то *x*ведет себя как локальное хранилище потока. Среди потоков, которые не связываются повторно *x*, он ведет себя как обычный глобальный поток : все эти потоки относятся к одной и той же привязке верхнего уровня *x*.

Динамические переменные могут использоваться для расширения контекста выполнения дополнительной контекстной информацией, которая неявно передается от функции к функции без необходимости появляться в качестве дополнительного параметра функции. Это особенно полезно, когда передача управления должна проходить через уровни несвязанного кода, который просто не может быть расширен дополнительными параметрами для передачи дополнительных данных. В такой ситуации обычно требуется глобальная переменная. Эта глобальная переменная должна быть сохранена и восстановлена, чтобы схема не нарушалась при рекурсии: об этом позаботится динамическое повторное связывание переменной. И эта переменная должна быть локальной для потока (иначе должен использоваться большой мьютекс), чтобы схема не нарушалась под потоками: реализации динамической области также могут позаботиться об этом.

В библиотеке Common Lisp есть много стандартных специальных переменных. Например, все стандартные потоки ввода-вывода хранятся в привязках верхнего уровня хорошо известных специальных переменных. Стандартный поток вывода хранится в * стандартном выводе *.

Предположим, функция foo записывает в стандартный вывод:

  (defun foo ()
    (format t "Hello, world"))

Чтобы записать его вывод в символьной строке, * стандартный вывод * можно связать со строковым потоком и вызвать:

  (with-output-to-string (*standard-output*)
    (foo))
 -> "Hello, world" ; gathered output returned as a string

Лексический

Common Lisp поддерживает лексические среды. Формально привязки в лексической среде имеют лексическую область видимости и могут иметь неопределенную или динамическую степень, в зависимости от типа пространства имен. Лексическая область видимости означает, что видимость физически ограничена блоком, в котором установлена ​​привязка. Ссылки, которые не текстуально (т. Е. Лексически) встроены в этот блок, просто не видят эту привязку.

Теги в TAGBODY имеют лексическую область видимости. Выражение (GO X) ошибочно, если оно не встроено в TAGBODY, который содержит метку X. Однако привязки меток исчезают, когда TAGBODY завершает свое выполнение, поскольку они имеют динамическую протяженность. Если этот блок кода повторно вводится посредством вызова лексического замыкания , то тело этого замыкания не может пытаться передать управление тегу через GO:

  (defvar *stashed*) ;; will hold a function

  (tagbody
    (setf *stashed* (lambda () (go some-label)))
    (go end-label) ;; skip the (print "Hello")
   some-label
    (print "Hello")
   end-label)
  -> NIL

Когда TAGBODY выполняется, он сначала оценивает форму setf, которая сохраняет функцию в специальной переменной * stashed *. Затем (go end-label) передает управление на end-label, пропуская код (выведите «Hello»). Поскольку конечная метка находится в конце тела тега, тело тега завершается, давая NIL. Предположим, что теперь вызывается ранее запомненная функция:

  (funcall *stashed*) ;; Error!

Это ошибочная ситуация. Ответом одной реализации является состояние ошибки, содержащее сообщение «GO: tagbody для тега SOME-LABEL уже оставлено». Функция попыталась оценить (go some-label), который лексически встроен в тело тега, и преобразуется в метку. Однако tagbody не выполняется (его размер закончился), и поэтому передача управления не может произойти.

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

Лексическая привязка - это режим привязки по умолчанию для переменных Common Lisp. Для отдельного символа его можно переключить в динамическую область видимости либо локальным объявлением, либо глобальным объявлением. Последнее может происходить неявно посредством использования таких конструкций, как DEFVAR или DEFPARAMETER. Это важная конвенция в Common Lisp программирования , что специальные (т.е. с динамической областью видимости) переменные имеют имена , которые начинаются и заканчиваются звездочкой сигилы * в том, что называется « Earmuff конвенции». При соблюдении этого соглашения создается отдельное пространство имен для специальных переменных, так что переменные, которые должны быть лексическими, не могут быть случайно сделаны специальными.

Лексическая область полезна по нескольким причинам.

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

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

В-третьих, что, возможно, наиболее важно, даже если лексические замыкания не используются, использование лексической области видимости изолирует программные модули от нежелательных взаимодействий. Из-за ограниченной видимости лексические переменные являются закрытыми. Если один модуль A связывает лексическую переменную X и вызывает другой модуль B, ссылки на X в B не будут случайно разрешаться в X, привязанный к A. B просто не имеет доступа к X. В ситуациях, когда дисциплинированные взаимодействия через переменную являются желательно, Common Lisp предоставляет специальные переменные. Специальные переменные позволяют модулю A устанавливать привязку для переменной X, которая видна другому модулю B, вызываемому из A. Возможность сделать это является преимуществом, и возможность предотвратить это также является преимуществом; следовательно, Common Lisp поддерживает как лексическую, так и динамическую область видимости .

Макросы

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

Типичное использование макросов в Лиспе:

  • новые структуры управления (пример: конструкции цикла, конструкции ветвления)
  • ограничивающие и связывающие конструкции
  • упрощенный синтаксис для сложного и повторяющегося исходного кода
  • формы определения верхнего уровня с побочными эффектами времени компиляции
  • программирование на основе данных
  • встроенные предметно-ориентированные языки (примеры: SQL, HTML, Prolog)
  • неявные формы завершения

Различные стандартные функции Common Lisp также должны быть реализованы в виде макросов, например:

  • стандартная setfабстракция, позволяющая настраивать расширения операторов присваивания / доступа во время компиляции
  • with-accessors, with-slots, with-open-fileИ другие подобные WITHмакросы
  • В зависимости от реализации, ifлибо condмакрос построен на другом, специальном операторе; whenи unlessсостоят из макросов
  • Мощный loopпредметно-ориентированный язык

Макросы определяются макросом defmacro . Специальный оператор macrolet позволяет определять локальные (лексический контекстные) макрокоманды. Также можно определить макросы для символов с помощью define-symbol-macro и symbol-macrolet .

В книге Пола Грэма « О Лиспе» подробно описано использование макросов в Common Lisp. Книга Дуга Хойта « Let Over Lambda» расширяет обсуждение макросов, утверждая, что «макросы - это единственное величайшее преимущество Lisp как языка программирования и единственное величайшее преимущество любого языка программирования». Хойт приводит несколько примеров итеративной разработки макросов.

Пример использования макроса для определения новой структуры управления

Макросы позволяют программистам на Лиспе создавать новые синтаксические формы на языке. Типичное использование - создание новых структур управления. Пример макроса предоставляет untilконструкцию цикла. Синтаксис:

(until test form*)

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

(defmacro until (test &body body)
  (let ((start-tag (gensym "START"))
        (end-tag   (gensym "END")))
    `(tagbody ,start-tag
              (when ,test (go ,end-tag))
              (progn ,@body)
              (go ,start-tag)
              ,end-tag)))

tagbody - это примитивный специальный оператор Common Lisp, который дает возможность присваивать имена тегам и использовать форму go для перехода к этим тегам. Кавычка ` предоставляет обозначения , что обеспечивает шаблоны кода, где значение формы предшествует запятую заполняется. Формы предшествуют запятую и при-знак сращены в. Форме tagbody тестирует граничное условие. Если условие истинно, выполняется переход к конечному тегу. В противном случае выполняется указанный основной код, а затем выполняется переход к начальному тегу.

Пример формы , используя выше до макро:

(until (= (random 10) 0)
  (write-line "Hello"))

Код можно расширить с помощью функции macroexpand-1 . Расширение для приведенного выше примера выглядит так:

(TAGBODY
 #:START1136
 (WHEN (ZEROP (RANDOM 10))
   (GO #:END1137))
 (PROGN (WRITE-LINE "hello"))
 (GO #:START1136)
 #:END1137)

Во время расширения макроса значение переменной test равно (= (random 10) 0), а значение тела переменной - ((строка записи «Hello»)) . Тело - это список форм.

Символы обычно автоматически изменяются. Расширение использует TAGBODY с двумя метками. Символы для этих этикеток вычисляются GENSYM и не интернированы ни в какой пакет. Две формы go используют эти теги для перехода. Поскольку tagbody - это примитивный оператор в Common Lisp (а не макрос), он не будет расширен во что-то еще. В развернутой форме используется макрос when , который также будет развернут. Полное раскрытие исходной формы называется обходом кода .

В полностью развернутой ( проходящей ) форме форма when заменяется примитивной, если :

(TAGBODY
 #:START1136
 (IF (ZEROP (RANDOM 10))
     (PROGN (GO #:END1137))
   NIL)
 (PROGN (WRITE-LINE "hello"))
 (GO #:START1136))
 #:END1137)

Все макросы должны быть раскрыты, прежде чем исходный код, содержащий их, можно будет оценить или скомпилировать в обычном режиме. Макросы можно рассматривать как функции, которые принимают и возвращают S-выражения - аналогично абстрактным синтаксическим деревьям , но не ограничиваясь ими. Эти функции вызываются перед оценщиком или компилятором для создания окончательного исходного кода. Макросы написаны на обычном Common Lisp и могут использовать любой доступный Common Lisp (или сторонний) оператор.

Переменный захват и затенение

Макросы Common Lisp способны к так называемому захвату переменных , когда символы в теле расширения макроса совпадают с символами в вызывающем контексте, что позволяет программисту создавать макросы, в которых различные символы имеют особое значение. Термин « захват переменной» несколько вводит в заблуждение, потому что все пространства имен уязвимы для нежелательного захвата, включая пространство имен оператора и функции, пространство имен метки tagbody, тег catch, обработчик условий и пространства имен перезапуска.

Захват переменных может привести к дефектам программного обеспечения. Это происходит одним из следующих двух способов:

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

Диалект Scheme в Lisp предоставляет систему записи макросов, которая обеспечивает ссылочную прозрачность, которая устраняет оба типа проблем захвата. Этот тип макросистемы иногда называют «гигиеническим», в частности, его сторонники (которые считают макросистемы, не решающие эту проблему автоматически, антисанитарными).

В Common Lisp макросигиена обеспечивается одним из двух способов.

Один из подходов - использовать генетические символы : гарантированно уникальные символы, которые можно использовать в макрорасширении без угрозы захвата. Использование генсимов в определении макроса - это ручная работа, но можно написать макросы, которые упростят создание и использование генсимов. Генсимы легко решают захват типа 2, но они не применимы к захвату типа 1 таким же образом, потому что расширение макроса не может переименовать мешающие символы в окружающем коде, которые захватывают его ссылки. Gensyms можно использовать для обеспечения стабильных псевдонимов для глобальных символов, которые необходимы для макрорасширения. Расширение макроса будет использовать эти секретные псевдонимы, а не хорошо известные имена, поэтому переопределение хорошо известных имен не окажет вредного воздействия на макрос.

Другой подход - использовать пакеты. Макрос, определенный в собственном пакете, может просто использовать внутренние символы этого пакета в своем расширении. Использование пакетов касается захвата типа 1 и типа 2.

Однако пакеты не решают проблему захвата 1-го типа ссылок на стандартные функции и операторы Common Lisp. Причина в том, что использование пакетов для решения проблем захвата вращается вокруг использования частных символов (символов в одном пакете, которые не импортируются или иным образом не становятся видимыми в других пакетах). В то время как символы библиотеки Common Lisp являются внешними и часто импортируются или становятся видимыми в пользовательских пакетах.

Ниже приведен пример нежелательного захвата в пространстве имен операторов, происходящего при раскрытии макроса:

 ;; expansion of UNTIL makes liberal use of DO
 (defmacro until (expression &body body)
   `(do () (,expression) ,@body))

 ;; macrolet establishes lexical operator binding for DO
 (macrolet ((do (...) ... something else ...))
   (until (= (random 10) 0) (write-line "Hello")))

untilМакрос будет расширяться в форме, вызовы doкоторых предназначен для обозначения стандартного макроса Common Lisp do. Однако в этом контексте doможет иметь совершенно другое значение, поэтому untilможет работать некорректно.

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

Система условий

Система условий отвечает за обработку исключений в Common Lisp. Он предоставляет условия , обработчики и перезапуск . Условия - это объекты, описывающие исключительную ситуацию (например, ошибку). Если указано условие , система Common Lisp ищет обработчик для этого типа условия и вызывает обработчик. Теперь обработчик может искать перезапуски и использовать один из этих перезапусков для автоматического устранения текущей проблемы, используя такую ​​информацию, как тип условия и любую соответствующую информацию, предоставленную как часть объекта условия, и вызывать соответствующую функцию перезапуска.

Эти перезапуски, если они не обрабатываются кодом, могут быть представлены пользователям (как часть пользовательского интерфейса, например, отладчика), чтобы пользователь мог выбрать и вызвать один из доступных перезапусков. Поскольку обработчик условий вызывается в контексте ошибки (без раскрутки стека), полное восстановление после ошибки возможно во многих случаях, когда другие системы обработки исключений уже завершили бы текущую процедуру. Сам отладчик также можно настроить или заменить с помощью *debugger-hook*динамической переменной. Код, находящийся в формах защиты от размотки, таких как финализаторы, также будет выполняться соответствующим образом, несмотря на исключение.

В следующем примере (с использованием Symbolics Genera ) пользователь пытается открыть файл в функциональном тесте Lisp, вызываемом из Read-Eval-Print-LOOP ( REPL ), когда файл не существует. Система Lisp предлагает четыре перезапуска. Пользователь выбирает Retry OPEN, используя перезапуск с другим именем пути, и вводит другое имя пути (lispm-init.lisp вместо lispm-int.lisp). Код пользователя не содержит кода обработки ошибок. Весь код обработки ошибок и перезапуска обеспечивается системой Lisp, которая может обрабатывать и исправлять ошибку, не прерывая пользовательский код.

Command: (test ">zippy>lispm-int.lisp")

Error: The file was not found.
       For lispm:>zippy>lispm-int.lisp.newest

LMFS:OPEN-LOCAL-LMFS-1
   Arg 0: #P"lispm:>zippy>lispm-int.lisp.newest"

s-A, <Resume>: Retry OPEN of lispm:>zippy>lispm-int.lisp.newest
s-B:           Retry OPEN using a different pathname
s-C, <Abort>:  Return to Lisp Top Level in a TELNET server
s-D:           Restart process TELNET terminal

-> Retry OPEN using a different pathname
Use what pathname instead [default lispm:>zippy>lispm-int.lisp.newest]:
   lispm:>zippy>lispm-init.lisp.newest

...the program continues

Общая объектная система Lisp (CLOS)

Common Lisp включает набор инструментов для объектно-ориентированного программирования , объектную систему Common Lisp или CLOS , которая является одной из самых мощных объектных систем, доступных на любом языке. Например, Питер Норвиг объясняет, сколько шаблонов проектирования проще реализовать на динамическом языке с функциями CLOS (множественное наследование, миксины, мультиметоды, метаклассы, комбинации методов и т. Д.). Было предложено включить несколько расширений Common Lisp для объектно-ориентированного программирования в стандарт ANSI Common Lisp, но в конечном итоге CLOS была принята в качестве стандартной объектной системы для Common Lisp. CLOS - это система динамических объектов с множественной диспетчеризацией и множественным наследованием , которая радикально отличается от средств ООП, имеющихся в статических языках, таких как C ++ или Java . Как динамическая объектная система, CLOS позволяет вносить изменения во время выполнения в общие функции и классы. Методы могут быть добавлены и удалены, классы могут быть добавлены и переопределены, объекты могут быть обновлены для изменений класса, а класс объектов может быть изменен.

CLOS интегрирован в ANSI Common Lisp. Общие функции могут использоваться как обычные функции и представляют собой первоклассный тип данных. Каждый класс CLOS интегрирован в систему типов Common Lisp. Многие типы Common Lisp имеют соответствующий класс. Для Common Lisp существует больше возможностей использовать CLOS. В спецификации не говорится, реализуются ли условия с помощью CLOS. Пути и потоки могут быть реализованы с помощью CLOS. Эти дополнительные возможности использования CLOS для ANSI Common Lisp не являются частью стандарта. Фактические реализации Common Lisp используют CLOS для имен путей, потоков, ввода-вывода, условий, реализации самого CLOS и многого другого.

Компилятор и интерпретатор

Интерпретатор Лиспа непосредственно выполняет исходный код Лиспа, предоставленный как объекты Лиспа (списки, символы, числа, ...), считанные из s-выражений. Компилятор Lisp генерирует байт-код или машинный код из исходного кода Lisp. Common Lisp позволяет компилировать как отдельные функции Lisp в памяти, так и компилировать целые файлы во внешне сохраненный скомпилированный код ( файлы fasl ).

Несколько реализаций более ранних диалектов Лиспа предоставляли как интерпретатор, так и компилятор. К сожалению, часто семантика была иной. Эти более ранние Лиспы реализовали лексическую область видимости в компиляторе и динамическую область видимости в интерпретаторе. Common Lisp требует, чтобы и интерпретатор, и компилятор по умолчанию использовали лексическую область видимости. Стандарт Common Lisp описывает семантику интерпретатора и компилятора. Компилятор может быть вызван с помощью функции compile для отдельных функций и с использованием файла компиляции функции для файлов. Common Lisp допускает объявление типов и предоставляет способы влиять на политику генерации кода компилятора. Для последних различных качеств оптимизации могут быть заданы значения от 0 (не важно) до 3 (наиболее важно): скорость , пространство , безопасность , отладка и скорость компиляции .

Существует также функция для оценки коды Lisp: eval. evalпринимает код как предварительно проанализированные s-выражения, а не, как в некоторых других языках, как текстовые строки. Таким образом, код может быть построен с помощью обычных функций Лиспа для построения списков и символов, а затем этот код может быть оценен с помощью функции eval. Несколько реализаций Common Lisp (например, Clozure CL и SBCL) реализуются evalс использованием своего компилятора. Таким образом код компилируется, даже если он оценивается с помощью функции eval.

Компилятор файла вызывается с помощью функции compile-file . Сгенерированный файл со скомпилированным кодом называется файлом fasl (из быстрой загрузки ). Эти файлы 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 +year-size+ 365)

(defun birthday-paradox (probability number-of-people)
  (let ((new-probability (* (/ (- +year-size+ number-of-people)
                               +year-size+)
                            probability)))
    (if (< new-probability 0.5)
        (1+ number-of-people)
        (birthday-paradox new-probability (1+ number-of-people)))))

Вызов функции примера с помощью REPL (цикл печати Read Eval):

CL-USER > (birthday-paradox 1.0 1)
23

Сортировка списка объектов человека

Мы определяем класс personи метод для отображения имени и возраста человека. Затем мы определяем группу людей как список personобъектов. Затем мы перебираем отсортированный список.

(defclass person ()
  ((name :initarg :name :accessor person-name)
   (age  :initarg :age  :accessor person-age))
  (:documentation "The class PERSON with slots NAME and AGE."))

(defmethod display ((object person) stream)
  "Displaying a PERSON object to an output stream."
  (with-slots (name age) object
    (format stream "~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))
  "A list of PERSON objects.")

(dolist (person (sort (copy-list *group*)
                      #'>
                      :key #'person-age))
  (display person *standard-output*)
  (terpri))

Он печатает три имени по убыванию возраста.

Bob (33)
Ash (23)
Chris (16)

Возведение в степень возведением в квадрат

Демонстрируется использование макроса LOOP:

(defun power (x n)
  (loop with result = 1
        while (plusp n)
        when (oddp n) do (setf result (* result x))
        do (setf x (* x x)
                 n (truncate n 2))
        finally (return result)))

Пример использования:

CL-USER > (power 2 200)
1606938044258990275541962092341162602522202993782792835301376

Сравните со встроенным возведением в степень:

CL-USER > (= (expt 2 200) (power 2 200))
T

Найдите список доступных оболочек

WITH-OPEN-FILE - это макрос, который открывает файл и предоставляет поток. Когда форма возвращается, файл автоматически закрывается. FUNCALL вызывает объект функции. LOOP собирает все строки, соответствующие предикату.

(defun list-matching-lines (file predicate)
  "Returns a list of lines in file, for which the predicate applied to
 the line returns T."
  (with-open-file (stream file)
    (loop for line = (read-line stream nil nil)
          while line
          when (funcall predicate line)
          collect it)))

Функция AVAILABLE-SHELLS вызывает вышеупомянутую функцию LIST-MATCHING-LINES с именем пути и анонимной функцией в качестве предиката. Предикат возвращает путь к оболочке или NIL (если строка не является именем файла оболочки).

(defun available-shells (&optional (file #p"/etc/shells"))
  (list-matching-lines
   file
   (lambda (line)
     (and (plusp (length line))
          (char= (char line 0) #\/)
          (pathname
           (string-right-trim '(#\space #\tab) line))))))

Пример результатов (в Mac OS X 10.6):

CL-USER > (available-shells)
(#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 ) по умолчанию использует область видимости лексических переменных как для интерпретируемого, так и для компилируемого кода.

Большинство систем Лиспа, чьи проекты способствовали развитию 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, тогда как в схеме все значения , отличные от # f, обрабатываются как истинные. Эти соглашения позволяют некоторым операторам на обоих языках служить как предикатами (отвечая на вопрос с логическим значением), так и возвращать полезное значение для дальнейших вычислений, но в Scheme значение '(), которое эквивалентно NIL в Common Lisp, оценивается как истинное. в логическом выражении.

Наконец, документы стандартов Scheme требуют оптимизации хвостового вызова , чего нет в стандарте CL. Большинство реализаций CL предлагают оптимизацию хвостового вызова, хотя часто только тогда, когда программист использует директиву оптимизации. Тем не менее, общий CL стиль кодирования не способствует повсеместное использование рекурсии , что схема стиль предпочитает-то , что программист Схема выразит с хвостом рекурсии, пользователь CL обычно выражают с помощью итеративного выражения в do, dolist, loopили (совсем недавно) с iterateпакет.

Реализации

См. Категория реализации Common Lisp .

Common Lisp определяется спецификацией (например, Ada и C ), а не одной реализацией (например, Perl ). Существует множество реализаций и стандартных деталей, в которых они могут действительно различаться.

Кроме того, реализации, как правило, идут с расширениями, которые предоставляют функции, не охваченные стандартом:

  • Интерактивный верхний уровень (REPL)
  • Вывоз мусора
  • Отладчик, шаговый двигатель и инспектор
  • Слабые структуры данных (хеш-таблицы)
  • Расширяемые последовательности
  • Расширяемый цикл
  • Доступ к среде
  • CLOS Мета-объектный протокол
  • Расширяемые потоки на основе CLOS
  • Система условий на основе CLOS
  • Сетевые потоки
  • Постоянное закрытие
  • Поддержка Unicode
  • Интерфейс на иностранном языке (часто на C)
  • Интерфейс операционной системы
  • Интерфейс Java
  • Потоки и многопроцессорность
  • Доставка приложений (приложения, динамические библиотеки)
  • Сохранение изображений

Бесплатные библиотеки программного обеспечения с открытым исходным кодом были созданы для поддержки расширений Common Lisp переносимым способом и, в первую очередь, находятся в репозиториях проектов Common-Lisp.net и CLOCC (Common Lisp Open Code Collection).

Реализации Common Lisp могут использовать любое сочетание компиляции собственного кода, компиляции или интерпретации байтового кода. Common Lisp был разработан для поддержки инкрементных компиляторов , компиляторов файлов и блочных компиляторов. Стандартные объявления для оптимизации компиляции (например, встраивание функций или специализация типов) предлагаются в спецификации языка. Большинство реализаций Common Lisp компилируют исходный код в собственный машинный код . Некоторые реализации могут создавать (оптимизированные) автономные приложения. Другие компилируются в интерпретируемый байт-код , который менее эффективен, чем собственный код, но облегчает переносимость двоичного кода. Некоторые компиляторы компилируют код Common Lisp в код C. Ошибочное представление о том, что Lisp является чисто интерпретируемым языком, скорее всего, связано с тем, что среды Lisp предоставляют интерактивные подсказки и этот код компилируется один за другим, поэтапно. В Common Lisp широко используется инкрементная компиляция.

Некоторые реализации на основе Unix ( CLISP , SBCL ) могут использоваться в качестве языка сценариев ; то есть вызывается системой прозрачно, как интерпретатор оболочки Perl или Unix .

Список реализаций

Коммерческие реализации

Аллегро Common Lisp
для Microsoft Windows, FreeBSD, Linux, Apple macOS и различных вариантов UNIX. Allegro CL предоставляет интегрированную среду разработки (IDE) (для Windows и Linux) и обширные возможности для доставки приложений.
Жидкий Common Lisp
ранее назывался Lucid Common Lisp . Только обслуживание, новых выпусков нет.
LispWorks
для Microsoft Windows, FreeBSD, Linux, Apple macOS, iOS, Android и различных вариантов UNIX. LispWorks предоставляет интегрированную среду разработки (IDE) (доступную для всех платформ, но не для iOS и Android) и обширные возможности для доставки приложений.
мокл
для iOS, Android и macOS.
Open Genera
для DEC Alpha.
Scieneer Common Lisp
который разработан для высокопроизводительных научных вычислений.

Свободно распространяемые реализации

Вооруженный медведь Common Lisp (ABCL)
Реализация CL, работающая на виртуальной машине Java . Он включает компилятор байтового кода Java и обеспечивает доступ к библиотекам Java из CL. Это было прежде всего компонента вооруженных Медведя J редактора .
CLISP
Реализация, компилирующая байт-код, переносимая и работающая в нескольких Unix и Unix-подобных системах (включая macOS ), а также в Microsoft Windows и некоторых других системах.
Закрытие CL (CCL)
Первоначально это была бесплатная вилка Macintosh Common Lisp с открытым исходным кодом . Как следует из этой истории, CCL был написан для Macintosh, но Clozure CL теперь работает на macOS , FreeBSD , Linux , Solaris и Windows . 32- и 64-разрядные порты x86 поддерживаются на каждой платформе. Дополнительно есть порты Power PC для Mac OS и Linux. CCL ранее был известен как OpenMCL, но это имя больше не используется, чтобы избежать путаницы с версией Macintosh Common Lisp с открытым исходным кодом.
CMUCL
Первоначально из Университета Карнеги-Меллона , в настоящее время поддерживается как бесплатное программное обеспечение с открытым исходным кодом группой добровольцев. CMUCL использует быстрый компилятор машинного кода. Он доступен в Linux и BSD для Intel x86; Linux для Alpha; macOS для Intel x86 и PowerPC; и Solaris, IRIX и HP-UX на своих собственных платформах.
Корман Common Lisp
для Microsoft Windows. В январе 2015 года Corman Lisp был опубликован под лицензией MIT.
Встраиваемый Common Lisp (ECL)
ECL включает интерпретатор и компилятор байт-кода. Он также может компилировать код Lisp в машинный код через компилятор C. Затем ECL компилирует код Lisp в C, компилирует код C с помощью компилятора C и затем может загрузить полученный машинный код. Также можно встраивать ECL в программы на C и код на C в программы Common Lisp.
GNU Common Lisp (GCL)
В GNU Lisp компилятор проекта. Еще не полностью совместимый с ANSI, GCL, тем не менее, является предпочтительной реализацией для нескольких крупных проектов, включая математические инструменты Maxima , AXIOM и (исторически) ACL2 . GCL работает в Linux под одиннадцатью различными архитектурами, а также под Windows, Solaris и FreeBSD .
Macintosh Common Lisp (MCL)
Версия 5.2 для компьютеров Apple Macintosh с процессором PowerPC под управлением Mac OS X является открытым исходным кодом. RMCL (на основе MCL 5.2) работает на компьютерах Apple Macintosh на базе Intel с использованием двоичного транслятора Rosetta от Apple.
ManKai Common Lisp (MKCL)
Филиал ECL . MKCL подчеркивает надежность, стабильность и общее качество кода за счет сильно переработанной, изначально многопоточной системы времени выполнения. В Linux MKCL представляет собой полностью совместимую с POSIX систему времени выполнения.
Мовиц
Реализует среду Lisp для компьютеров x86, не полагаясь на какую-либо базовую ОС.
Поплог
Poplog реализует версию CL с POP-11 и, возможно, Prolog и Standard ML (SML), позволяя программировать на разных языках. Для всех языком реализации является POP-11, который компилируется постепенно. Он также имеет встроенный редактор, подобный Emacs, который взаимодействует с компилятором.
Steel Bank Common Lisp (SBCL)
Ветка от CMUCL . «Вообще говоря, SBCL отличается от CMU CL большим упором на ремонтопригодность». SBCL работает на платформах CMUCL, кроме HP / UX; кроме того, он работает в Linux для AMD64, PowerPC, SPARC, MIPS, Windows x86 и имеет экспериментальную поддержку для работы в Windows AMD64. SBCL по умолчанию не использует интерпретатор; все выражения компилируются в машинный код, если пользователь не включает интерпретатор. Компилятор SBCL генерирует быстрый машинный код в соответствии с предыдущей версией The Computer Language Benchmarks Game .
Ufasoft Common Lisp
порт CLISP для платформы Windows с ядром, написанным на C ++.

Другие реализации

Остин Киото Common Lisp
эволюция Киото Common Lisp от Билла Шелтер
Бабочка Common Lisp
реализация, написанная на схеме для многопроцессорного компьютера BBN Butterfly
CLICC
компилятор Common Lisp to C
CLOE
Common Lisp для ПК от Symbolics
Codemist Common Lisp
используется для коммерческой версии системы компьютерной алгебры Axiom
ExperCommon Lisp
ранняя реализация для Apple Macintosh от ExperTelligence
Золотой Common Lisp
реализация для ПК от GoldHill Inc.
Ибуки Common Lisp
коммерческая версия Kyoto Common Lisp
Киотский Common Lisp
первый компилятор Common Lisp, который использовал C в качестве целевого языка. GCL, ECL и MKCL происходят от этой реализации Common Lisp.
L
маленькая версия Common Lisp для встроенных систем, разработанная IS Robotics, теперь iRobot
Машины на Лиспе (от Symbolics , TI и Xerox)
предоставили реализации Common Lisp в дополнение к их родному диалекту Lisp (Lisp Machine Lisp или Interlisp). Также был доступен CLOS. Symbolics предоставляет расширенную версию Common Lisp.
Процион Common Lisp
реализация для Windows и Mac OS, которую Франц использовал для своего Windows-порта Allegro CL.
Звездный Сапфир Обычный LISP
реализация для ПК
SubL
вариант Common Lisp, используемый для реализации системы Cyc, основанной на знаниях
Common Lisp верхнего уровня
ранняя реализация для параллельного выполнения
WCL
реализация разделяемой библиотеки
Vax Common Lisp
Реализация Digital Equipment Corporation, работающая в системах VAX с VMS или ULTRIX
XLISP
реализация, написанная Дэвидом Бетцем

Приложения

Смотрите категорию программного обеспечения Common Lisp .

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

Common Lisp используется во многих коммерческих приложениях, включая Yahoo! Интернет-магазин магазина, в котором первоначально участвовал Пол Грэм, а затем был переписан на C ++ и Perl. Другие известные примеры включают:

  • ACT-R - когнитивная архитектура, используемая в большом количестве исследовательских проектов.
  • Помощник авторизатора, большая система на основе правил, используемая American Express для анализа кредитных запросов.
  • Cyc , долгосрочный проект по созданию системы, основанной на знаниях, которая предоставляет огромное количество здравого смысла.
  • Gensym G2 , экспертная система в реальном времени и движок бизнес-правил
  • Genworks GDL , основанный на ядре Gendl с открытым исходным кодом.
  • Среда разработки для серии видеоигр Jak and Daxter , разработанная Naughty Dog .
  • Система поиска дешевых билетов ITA Software , используемая туристическими сайтами, такими как Orbitz и Kayak.com, и такими авиакомпаниями, как American Airlines , Continental Airlines и US Airways .
  • Mirai , набор для 3D-графики. Он был использован для анимации лица Голлума в фильме «Властелин колец: Две башни».
  • Система проверки прототипов (PVS), механизированная среда для формальной спецификации и проверки.
  • PWGL - это сложная среда визуального программирования, основанная на Common Lisp, используемая в компьютерной композиции и синтезе звука.
  • Piano, полный пакет анализа самолетов, написанный на Common Lisp, используемый такими компаниями, как Boeing , Airbus , Northrop Grumman .
  • Grammarly , англоязычная платформа для улучшения письма, имеет ядро ​​грамматического движка, написанного на Common Lisp.
  • Инструмент динамического анализа и перепланирования (DART), который, как говорят, один только окупил с 1991 по 1995 годы все тридцать лет инвестиций DARPA в исследования ИИ.
  • « Удаленный агент » НАСА (Лаборатории реактивного движения) , отмеченная наградами программа Common Lisp для автопилота космического корабля Deep Space One.
  • SigLab, платформа Common Lisp для обработки сигналов, используемая в противоракетной обороне, построенная Raytheon
  • Система планирования миссий NASA Mars Pathfinder
  • SPIKE, система планирования для земных или космических обсерваторий и спутников, в частности с Hubble Space Telescope., Написанный в Common Lisp
  • Common Lisp был использован для создания прототипа сборщика мусора Microsoft .NET Common Language Runtime.
  • Первоначальная версия Reddit , хотя позже разработчики перешли на Python из-за отсутствия библиотек для Common Lisp, согласно официальному сообщению в блоге соучредителя Reddit Стива Хаффмана .

Также существуют приложения с открытым исходным кодом, написанные на Common Lisp, такие как:

Библиотеки

С 2011 года Зак Бин при поддержке Common Lisp Foundation поддерживает менеджер библиотеки Quicklisp . Он позволяет автоматически загружать, устанавливать и загружать более 3600 библиотек, каждая из которых необходима для работы более чем с одной реализацией Common Lisp и иметь лицензию, разрешающую их распространение.

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

Ссылки

Библиография

Хронологический список опубликованных (или готовящихся к публикации) книг о Common Lisp (языке) или о программировании на Common Lisp (особенно о программировании на основе ИИ).

внешние ссылки