stringtranslate.com

Общий Лисп

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может содержать неупакованное целое число в диапазоне, поддерживаемом оборудованием и реализацией, что позволяет выполнять более эффективную арифметику, чем на больших целых числах или типах произвольной точности. Аналогично, компилятору можно указать на основе модуля или функции, какой тип уровня безопасности требуется, используя декларацию optimize .

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 Object System, Condition System для обработки ошибок, интерфейс к красивому принтеру и многое другое. Но CLtL2 не описывает окончательный стандарт ANSI Common Lisp и, таким образом, не является документацией ANSI Common Lisp. Окончательный стандарт ANSI Common Lisp был опубликован в 1994 году. С тех пор никаких обновлений стандарта не публиковалось. Различные расширения и улучшения Common Lisp (примерами являются Unicode, Concurrency, CLOS-based IO) были предоставлены реализациями и библиотеками .

Синтаксис

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

 ( + 2 2 ) ; складывает 2 и 2, давая 4. Имя функции — '+'. В Lisp нет операторов как таковых.   
 ( defvar *x* ) ; Гарантирует, что переменная *x* существует, ; не присваивая ей значения. Звездочки являются частью ; имени, по соглашению обозначая специальную (глобальную) переменную. ; Символ *x* также настоящим наделяется свойством, что ; последующие его привязки являются динамическими, а не лексическими. ( setf *x* 42.1 ) ; Устанавливает переменную *x* в значение с плавающей точкой 42.1          
 ;; Определите функцию, которая возводит число в квадрат: ( defun square ( x ) ( * x x ))      
 ;; Выполнить функцию: ( квадрат 3 ) ; Возвращает 9   
 ;; Конструкция 'let' создает область действия для локальных переменных. Здесь ;; переменная 'a' привязана к 6, а переменная 'b' привязана к ;; 4. Внутри 'let' находится 'body', в котором возвращается последнее вычисленное значение. ;; Здесь возвращается результат сложения a и b из выражения 'let'. ;; Переменные a и b имеют лексическую область действия, если только символы не были ;; помечены как специальные переменные (например, предшествующим DEFVAR). ( let (( a 6 ) ( b 4 )) ( + a b )) ; возвращает 10              

Типы данных

В Common Lisp имеется множество типов данных .

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

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

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

Тип символа является общим для языков Lisp, но в значительной степени неизвестен за их пределами. Символ — это уникальный именованный объект данных, состоящий из нескольких частей: имя, значение, функция, список свойств и пакет. Из них наиболее важны ячейка значения и ячейка функции . Символы в Lisp часто используются аналогично идентификаторам в других языках: для хранения значения переменной; однако есть и много других применений. Обычно, когда символ оценивается, его значение возвращается. Некоторые символы оцениваются сами по себе, например, все символы в ключевом слове package являются самооценивающимися. Булевы значения в 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 состоят из cons , иногда называемых cons-ячейками или парами . Cons — это структура данных с двумя слотами, называемыми car и cdr . Список — это связанная цепочка cons или пустой список. Car каждого cons ссылается на члена списка (возможно, на другой список). Cdr каждого cons ссылается на следующий cons — за исключением последнего cons в списке, cdr которого ссылается на nilзначение. Cons также можно легко использовать для реализации деревьев и других сложных структур данных; хотя обычно рекомендуется вместо этого использовать экземпляры структур или классов. Также можно создавать циклические структуры данных с помощью cons.

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

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

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

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

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

Функции

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

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

 ;; Сортирует список, используя функции > и < в качестве оператора отношения. ( sort ( list 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 ' ( 9 A ) ' ( 3 B ) ' ( 4 C )) #' < :key #' first ) ; Возвращает ((3 B) (4 C) (9 A))            

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

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

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

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

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

 ( defun квадрат ( x ) ( * x x ))     

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

 ( defun square ( x ) "Вычисляет квадрат числа с плавающей точкой 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.

 ( флет (( квадрат ( x ) ( * x x ))) ( квадрат 3 ))       

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

Определение универсальных функций и методов

Макрос defgenericопределяет общие функции . Общие функции представляют собой набор методов . Макрос defmethodопределяет методы.

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

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

 ( defgeneric add ( a b ))   
 ( defmethod add (( число ) ( число b ) ) ( + a b ))        
 ( defmethod add (( вектор ) ( число b )) ( map 'vector ( лямбда ( n ) ( + n b ) ) a ))             
 ( defmethod add (( a vector ) ( b vector )) ( map 'vector #' + a b ))          
( defmethod add (( a string ) ( b string )) ( concatenate 'string 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"                        

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

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

Пространство имен для имен функций отделено от пространства имен для переменных данных. Это ключевое различие между 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] , где любое выражение всегда имеет одно первичное значение , но оно также может иметь любое количество вторичных значений , которые могут быть получены и проверены заинтересованными вызывающими. Эта концепция отличается от возврата значения списка, поскольку вторичные значения полностью необязательны и передаются через выделенный сторонний канал. Это означает, что вызывающие могут оставаться совершенно не осведомленными о наличии вторичных значений, если они им не нужны, и это делает удобным использование механизма для передачи информации, которая иногда полезна, но не всегда необходима. Например,

( пусть (( x 1266778 ) ( y 458 )) ( связывание-множественных-значений ( остаток от частного ) ( усечение x y ) ( формат nil "~A деленное на ~A равно ~A остаток ~A" x y остаток от частного )))                 ;;;; => "1266778 делится на 458, получается 2765, остаток 408"
( defun get-answer ( библиотека ) ( gethash 'answer библиотека 42 ))      ( defun the-answer-1 ( library ) ( format nil "Ответ ~A" ( get-answer library ))) ;;;; Возвращает "Ответ 42", если ОТВЕТ отсутствует в БИБЛИОТЕКЕ       ( defun the-answer-2 ( library ) ( multiple-value-bind ( answer sure-p ) ( get-answer library ) ( if ( not sure-p ) "Я не знаю" ( format nil "Ответ ~A" answer )))) ;;;; Возвращает "Я не знаю", если ОТВЕТ отсутствует в БИБЛИОТЕКЕ               

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

( defun magic-eight-ball () "Вернуть прогноз прогноза, с вероятностью в качестве вторичного значения" ( values ​​"Прогноз хороший" ( random 1.0 )))       ;;;; => "Перспективы хорошие" ;;;; => 0.3187

Другие типы

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

Объем

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

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

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

Определители сферы применения

Обстоятельства, определяющие область действия в Common Lisp, включают в себя:

Чтобы понять, на что ссылается символ, программист 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 () ( format t "Привет, мир" ))     

Чтобы захватить вывод в виде строки символов, *standard-output* можно привязать к потоку строк и вызвать:

 ( с-выводом-в-строку ( *стандартный-вывод* ) ( foo ))  
-> "Hello, world" ; собранные выходные данные возвращаются в виде строки

Лексический

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

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

 ( defvar *stashed* ) ;; будет содержать функцию   ( tagbody ( setf *stashed* ( lambda () ( go some-label ))) ( go end-label ) ;; пропустить (print "Hello") some-label ( print "Hello" ) end-label ) -> NIL               

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

 ( funcall *stashed* ) ;; Ошибка!  

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

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

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

Лексический охват полезен по нескольким причинам.

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

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

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

Макросы

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

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

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

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

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

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

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

(до тестовой формы*)

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

( 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 для перехода к этим тегам. Обратная кавычка ` обеспечивает нотацию, которая предоставляет шаблоны кода, в которых заполняются значения форм, предваряемых запятой. Формы, предваряемые запятой и знаком at, вставляются . Форма tagbody проверяет конечное условие. Если условие истинно, выполняется переход к конечному тегу. В противном случае выполняется предоставленный код тела, а затем выполняется переход к начальному тегу.

Пример использования приведенного выше макроса until :

( до ( = ( случайное число 10 ) 0 ) ( write-line "Привет" ))      

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

( ТЕГ #: START1136 ( КОГДА ( НОЛЬ ( СЛУЧАЙНЫЕ 10 )) ( GO #: END1137 )) ( PROGN ( СТРОКА ЗАПИСИ "привет" )) ( GO #: START1136 ) #: END1137 )             

Во время макрорасширения значение переменной test равно (= (random 10) 0), а значение переменной body равно ((write-line "Hello")) . Тело представляет собой список форм.

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

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

( ТЕГ #: START1136 ( IF ( НОЛЬ ( СЛУЧАЙНЫЕ 10 )) ( ПРОГНОЗ ( GO #: END1137 )) НОЛЬ ) ( ПРОГНОЗ ( СТРОКА ЗАПИСИ "привет" )) ( GO #: START1136 )) #: END1137 )               

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

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

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

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

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

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

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

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

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

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

 ;; расширение UNTIL позволяет свободно использовать DO ( defmacro until ( expression &body body ) ` ( do () ( , expression ) ,@ body ))          ;; macrolet устанавливает лексическую операторную привязку для 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. [18] Она предоставляет условия , обработчики и перезапуски . Условия — это объекты, описывающие исключительную ситуацию (например, ошибку). Если условие сигнализируется, система Common Lisp ищет обработчик для этого типа условия и вызывает обработчик. Теперь обработчик может искать перезапуски и использовать один из этих перезапусков для автоматического исправления текущей проблемы, используя такую ​​информацию, как тип условия и любую соответствующую информацию, предоставленную как часть объекта условия, и вызывать соответствующую функцию перезапуска.

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

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

Команда: (test >zippy>lispm-int.lisp")Ошибка: Файл не найден. Для lispm:>zippy>lispm-int.lisp.newestLMFS:ОТКРЫТЫЙ-ЛОКАЛЬНЫЙ-LMFS-1 Аргумент 0: #P"lispm:>zippy>lispm-int.lisp.newest"sA, <Resume>: Повторить попытку ОТКРЫТИЯ lispm:>zippy>lispm-int.lisp.newestsB: Повторите попытку OPEN, используя другой путьsC, <Abort>: Возврат на верхний уровень Lisp на сервере TELNETsD: Перезапустить процесс TELNET-терминала-> Повторите попытку OPEN, используя другой путьВместо этого используйте следующий путь [по умолчанию lispm:>zippy>lispm-int.lisp.newest]: lispm:>zippy>lispm-init.lisp.новейший...программа продолжается

Система объектов Common Lisp (CLOS)

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 ).

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

Также есть функция для оценки кода 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 +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 Print Loop):

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 ( format stream "~a (~a)" name age )))              ( defparameter *group* ( list ( make-instance 'person :name "Боб" :возраст 33 ) ( make-instance 'person :name "Крис" :возраст 16 ) ( make-instance 'person :name "Эш" :возраст 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 ) when ( oddp n ) do ( setf result ( * result x )) do ( setf x ( * x x ) n ( truncate n 2 )) finally ( return result )))                                 

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

CL-USER > ( мощность 2 200 ) 1606938044258990275541962092341162602522202993782792835301376    

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

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

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

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

( defun list-matching-lines ( file predicate ) "Возвращает список строк в файле, для которых предикат, примененный к строке, возвращает 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 > ( доступные-оболочки ) ( #P"/bin/bash" #P"/bin/csh " #P "/bin/ksh" #P"/bin/sh" #P"/bin/tcsh" #P"/bin/zsh" )       

Сравнение с другими Lisp-ами

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 исключительное использование лексически ограниченных переменных; вдохновение из ALGOL 68. CL также поддерживает динамически ограниченные переменные, но они должны быть явно объявлены как «специальные». Различий в области действия между интерпретаторами и компиляторами ANSI CL нет.

Common Lisp иногда называют Lisp-2 , а Scheme — Lisp-1 , ссылаясь на использование CL отдельных пространств имен для функций и переменных. (На самом деле, в CL много пространств имен, например, для тегов go, имен блоков и loopключевых слов). Между сторонниками CL и Scheme существует давний спор по поводу компромиссов, связанных с несколькими пространствами имен. В Scheme (в целом) необходимо избегать присвоения переменным имен, которые конфликтуют с функциями; функции Scheme часто имеют аргументы с именами lis, lst, или , lystчтобы не конфликтовать с системной функцией list. Однако в CL необходимо явно ссылаться на пространство имен функции при передаче функции в качестве аргумента, что также является распространенным явлением, как в sortпримере выше.

CL также отличается от Scheme обработкой булевых значений. Scheme использует специальные значения #t и #f для представления истинности и ложности. CL следует старому соглашению Lisp об использовании символов T и NIL, где NIL также обозначает пустой список. В CL любое значение, отличное от NIL, рассматривается как истинное с помощью условных операторов, таких как if, тогда как в Scheme все значения, отличные от #f, рассматриваются как истинные. Эти соглашения позволяют некоторым операторам в обоих языках служить как предикатами (отвечая на вопрос с булевским значением), так и возвращать полезное значение для дальнейших вычислений, но в Scheme значение '(), которое эквивалентно NIL в Common Lisp, оценивается как истинное в булевом выражении.

Наконец, документы стандартов 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]

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

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

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

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

Вооруженный медведь, общий шепелявый (ABCL)
Реализация CL, которая работает на виртуальной машине Java . [23] Она включает компилятор в байт-код Java и позволяет получать доступ к библиотекам Java из CL. Ранее она была просто компонентом редактора Armed Bear J Editor.
Застежка
Реализация на основе LLVM, которая легко взаимодействует с библиотеками C++. Работает на нескольких Unix и Unix-подобных системах (включая macOS ).
КЛИСП
Реализация компиляции байт-кода, переносимая и работающая на нескольких 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 на их нативных платформах.
Корман Комон Лисп
для Microsoft Windows. В январе 2015 года Corman Lisp был опубликован под лицензией MIT. [24]
Встраиваемый Common Lisp (ECL)
ECL включает в себя интерпретатор и компилятор байт-кода. Он также может компилировать код Lisp в машинный код через компилятор C. Затем ECL компилирует код Lisp в C, компилирует код C с помощью компилятора C и затем может загрузить полученный машинный код. Также возможно встраивать ECL в программы C , а код C в программы Common Lisp.
GNU Common Lisp (GCL)
Компилятор Lisp проекта GNU . Пока не полностью совместимый с 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 Общий Лисп (SBCL)
Ответвление от CMUCL . "В широком смысле, SBCL отличается от CMU CL большим акцентом на удобство обслуживания." [25] SBCL работает на платформах CMUCL, за исключением HP/UX; кроме того, он работает на Linux для AMD64, PowerPC, SPARC, MIPS, Windows x86 и AMD64. [26] SBCL по умолчанию не использует интерпретатор; все выражения компилируются в машинный код, если пользователь не включает интерпретатор. Компилятор SBCL генерирует быстрый машинный код в соответствии с предыдущей версией The Computer Language Benchmarks Game . [27]
Ufasoft Common Lisp
порт CLISP для платформы Windows с ядром, написанным на C++.

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

Остин Киото Common Lisp
эволюция Kyoto Common Lisp Билла Шелтера
Бабочка Common Lisp
реализация, написанная на Scheme для многопроцессорного компьютера BBN Butterfly [28] [29]
КЛИКК
компилятор Common Lisp в C [30]
ХЛОЕ
Common Lisp для ПК от Symbolics
Codemist Common Lisp
используется для коммерческой версии системы компьютерной алгебры Axiom [31] [32]
ExperCommon Lisp
ранняя реализация для Apple Macintosh от ExperTelligence
Золотой общий лисп
реализация для ПК от GoldHill Inc. [33] [34]
Ибуки Комон Лисп
коммерческая версия Kyoto Common Lisp
Киотский общий лисп
первый компилятор Common Lisp, использовавший C в качестве целевого языка. GCL, ECL и MKCL происходят от этой реализации Common Lisp.
Л
уменьшенная версия Common Lisp для встраиваемых систем, разработанная IS Robotics, теперь iRobot [35]
Машины Lisp (от Symbolics , TI [36] [37] и Xerox [38] )
предоставляли реализации Common Lisp в дополнение к их родному диалекту Lisp (Lisp Machine Lisp или Interlisp). CLOS также был доступен. Symbolics предоставляет улучшенную версию Common Lisp. [39] [40] [41]
Процион Common Lisp
реализация для Windows и Mac OS, используемая Franz для своего порта Allegro CL для Windows
Звездный сапфир Common LISP
реализация для ПК
SubL
вариант Common Lisp, используемый для реализации системы знаний Cyc [42]
Common Lisp верхнего уровня
ранняя реализация для одновременного выполнения [43]
ВКЛ
реализация общей библиотеки [44] [45]
VAX Common Lisp
Реализация корпорации Digital Equipment Corporation , работающая на системах VAX с VMS или ULTRIX
XLISP
реализация, написанная Дэвидом Бецом [46]

Приложения

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

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

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

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

Ссылки

  1. ^ "ANSI Standards Action - 28 декабря 2018 г." (PDF) . ansi.org . Архивировано (PDF) из оригинала 12 апреля 2021 г.
  2. ^ Цитата из обложки цитируемого стандарта. ANSI INCITS 226-1994 [S2008], продается на странице документа стандарта. Архивировано 27 сентября 2020 г. на Wayback Machine .
  3. ^ "CLHS: О Common Lisp HyperSpec (TM)". lispworks.com .
  4. ^ "CLHS: Раздел 1.1.2". lispworks.com .
  5. ^ "Common Lisp Implementations: A Survey". Архивировано из оригинала 21 апреля 2012 г. Получено 22 декабря 2007 г.
  6. ^ "Старые программы LISP по-прежнему работают в Common Lisp" . Получено 13 мая 2015 г.
  7. ^ «Корни «Yu-Shiang Lisp», письмо от Джона Л. Уайта, 1982». cmu.edu .
  8. ^ "Почтовый индекс". cl-su-ai.lisp.se .
  9. ^ Рефлекторный анти-LOOPизм и другие явления электронной почты: устные, письменные и электронные паттерны в компьютерно-опосредованной коммуникации, Джоанн Йейтс и Ванда Дж. Орликовски, 1993. Архивировано 8 августа 2012 г. на Wayback Machine
  10. ^ Стил, Гай Л. младший (15 августа 1982 г.). «Обзор COMMON LISP». Труды симпозиума ACM 1982 года по LISP и функциональному программированию — LFP '82 . ACM. стр. 98–107. doi :10.1145/800068.802140. ISBN 9780897910828. S2CID  14517358.
  11. Редди, Абишек (22 августа 2008 г.). «Особенности Common Lisp».
  12. ^ "Поддержка Unicode". Common Lisp Wiki . Получено 21 августа 2008 г.
  13. ^ Ричард П. Габриэль; Кент М. Питман (июнь 1988 г.). «Технические вопросы разделения в функциональных ячейках и ячейках значений». LISP и символьные вычисления . 1 (1): 81–101. doi :10.1007/bf01806178. S2CID  26716515.
  14. ^ «Common Lisp Hyperspec: Раздел 3.1.7».
  15. ^ "Common Lisp Hyperspec: Функция FLOOR".
  16. ^ "Common Lisp Hyperspec: Аксессор GETHASH".
  17. ^ "Let Over Lambda". letoverlambda.com .
  18. Питер Сейбел (7 апреля 2005 г.). Practical Common Lisp. Apress. ISBN 978-1-59059-239-7.
  19. ^ «Шаблоны проектирования в динамическом программировании». norvig.com .
  20. ^ "Common-Lisp.net".
  21. ^ «Коллекция открытого кода Common Lisp».
  22. ^ "32.6. Быстрый запуск поставки с помощью CLISP". clisp.cons.org .
  23. ^ «Вооруженный медведь, общая шепелявость».
  24. ^ "Исходные коды Corman Lisp теперь доступны". 5 января 2015 г.
  25. ^ "История и авторское право". Steel Bank Common Lisp .
  26. ^ "Таблица платформы". Steel Bank Common Lisp .
  27. ^ «Какие программы самые быстрые? – Computer Language Benchmarks Game». 20 мая 2013 г. Архивировано из оригинала 20 мая 2013 г.
  28. ^ "Пакет: lang/lisp/impl/bbn/". cs.cmu.edu .
  29. ^ "Последние разработки в Butterfly Lisp, 1987, Труды AAAI" (PDF) . aaai.org . Архивировано (PDF) из оригинала 11 октября 2015 г.
  30. ^ Burkart, O.; Goerigk, W.; Knutzen, H. (22 июня 1992 г.). "CLICC: Новый подход к компиляции программ Common Lisp в C". CiteSeerX 10.1.1.38.1282 .  {{cite journal}}: Цитировать журнал требует |journal=( помощь )
  31. ^ "codemist.co.uk". lisp.codemist.co.uk .
  32. ^ «Аксиома, 30-летний горизонт, страница 43» (PDF) .
  33. ^ "Золотой разработчик Common Lisp". goldhill-inc.com .
  34. ^ Golden Common LISP: практический подход, Дэвид Дж. Стил, июнь 2000 г., Addison Wesley Publishing Company
  35. ^ Брукс, Родни А.; и др., и др. (22 июня 1995 г.). "L – Common Lisp для встраиваемых систем". CiteSeerX 10.1.1.2.1953 .  {{cite journal}}: Цитировать журнал требует |journal=( помощь )
  36. ^ «Концепции программирования TI Explorer» (PDF) .
  37. ^ «Справочник TI Explorer Lisp» (PDF) .
  38. ^ «Заметки о выпуске Medley Lisp» (PDF) .
  39. ^ "Symbolics Common Lisp Dictionary" (PDF) . trailing-edge.com . Архивировано (PDF) из оригинала 22 апреля 2015 г.
  40. ^ "Symbolics Common Lisp Language Concepts" (PDF) . trailing-edge.com . Архивировано (PDF) из оригинала 22 апреля 2015 г.
  41. ^ "Symbolics Common Lisp Programming Constructs" (PDF) . trailing-edge.com . Архивировано (PDF) из оригинала 22 апреля 2015 г.
  42. ^ "Ссылка на SubL - Cycorp" . cyc.com .
  43. ^ "Top Level Inc. – Группа по сохранению программного обеспечения". softwarepreservation.org .
  44. ^ WCL: Создание эффективных приложений Common Lisp в Unix, Труды конференции ACM 1992 года по LISP и функциональному программированию, страницы 260–269
  45. ^ "commonlisp.net :: WCL". pgc.com . Архивировано из оригинала 5 апреля 2016 г. Получено 25 марта 2016 г.
  46. ^ "Пакет: lang/lisp/impl/xlisp/". cs.cmu.edu .
  47. ^ «Превзойти средние показатели». paulgraham.com .
  48. ^ "Authorizer's Assistant" (PDF) . aaai.org . Архивировано (PDF) из оригинала 4 июня 2011 г.
  49. Помощник авторизатора American Express. Архивировано 12 декабря 2009 г. на Wayback Machine.
  50. ^ Разработка приложений реального времени Архивировано 2 августа 2016 г. на Wayback Machine . Gensym. Получено 16 августа 2016 г.
  51. ^ "Genworks GDL".
  52. ^ «Опусмодус – Дом».
  53. PWGL – Home Архивировано 3 мая 2011 г. на Wayback Machine , получено 17 июля 2013 г.
  54. ^ ab "Аэрокосмическая промышленность – Common Lisp". lisp-lang.org .
  55. ^ «Пользователи фортепиано, получено со страницы производителя».
  56. ^ "Grammarly.com, Запуск Lisp в производстве". 26 июня 2015 г.
  57. ^ «Удаленный агент». ti.arc.nasa.gov .
  58. ^ «Шепелявость в JPL».
  59. ^ "Приложения клиентов Franz Inc: NASA". franz.com .
  60. ^ Spike Planning and Scheduling System. Stsci.edu. Получено 17 июля 2013 г.
  61. ^ "Приложения клиентов Franz Inc: Институт космических телескопов". franz.com .
  62. ^ «Как все началось… Также известное как рождение CLR». microsoft.com . 28 августа 2023 г.
  63. ^ Хаффман, Стив. "on lisp". Проголосовано . Архивировано из оригинала 17 мая 2018 г. Получено 11 мая 2019 г.
  64. ^ "Pgloader".
  65. ^ "Почему pgloader настолько быстрее". 14 мая 2014 г.

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

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

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