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

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

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

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

История

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

Документация по первому языку была опубликована в 1984 году под названием Common Lisp the Language (известная как CLtL1), первое издание. Второе издание (известное как CLtL2), опубликованное в 1990 году, включило в язык множество изменений, внесенных в процессе стандартизации ANSI Common Lisp: расширенный синтаксис LOOP, объектную систему Common Lisp, систему условий для обработки ошибок, интерфейс для красивый принтер и многое другое. Но CLtL2 не описывает окончательную версию стандарта ANSI Common Lisp и, следовательно, не является документацией ANSI Common Lisp. Окончательный стандарт ANSI Common Lisp был опубликован в 1994 году. С тех пор никаких обновлений стандарта не публиковалось. Различные расширения и улучшения Common Lisp (примеры — Unicode, Concurrency, ввод-вывод на основе CLOS) были предоставлены реализациями и библиотеками .

Синтаксис

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

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

Типы данных

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

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

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

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

Тип символа является общим для языков Лисп, но практически неизвестен за его пределами . Символ — это уникальный именованный объект данных, состоящий из нескольких частей: имени, значения, функции, списка свойств и пакета. Из них наиболее важными являются ячейка значения и ячейка функции . Символы в Лиспе часто используются аналогично идентификаторам в других языках: для хранения значения переменной; однако есть много других применений. Обычно при оценке символа возвращается его значение. Некоторые символы оцениваются сами по себе, например, все символы в пакете ключевых слов являются самооценочными. Логические значения в Common Lisp представлены символами самовычисления T и NIL. В Common Lisp есть пространства имен для символов, называемые «пакетами».

Доступен ряд функций для округления скалярных числовых значений различными способами. Функция roundокругляет аргумент до ближайшего целого числа, а промежуточные значения округляются до четного целого числа. Функции truncate, floorи ceilingокругляют в сторону нуля, вниз или вверх соответственно. Все эти функции возвращают отброшенную дробную часть как вторичное значение. Например, (floor -2.5)дает −3, 0,5; (ceiling -2.5)дает -2, -0,5; (round 2.5)дает 2, 0,5; и (round 3.5)дает 4, -0,5.

Структуры данных

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

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

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

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

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

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

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

Функции

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

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

 ;; Сортирует список, используя функции > и < в качестве оператора отношения. ( сортировка ( список 5 2 6 3 1 4 ) #' > ) ; Возвращает (6 5 4 3 2 1) ( sort ( list 5 2 6 3 1 4 ) #' < ) ; Возврат (1 2 3 4 5 6)                    
 ;; Сортирует список по первому элементу каждого подсписка. ( sort ( list ' ( 9A ) ' ( 3B ) ' ( 4C ) ) #' < : key # ' first ) ; Возвращает ((3 Б) (4 С) (9 А))            

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

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

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

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

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

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

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

 ( defun Square ( x ) "Вычисляет квадрат одинарного числа с плавающей запятой x." ( объявление ( одинарное число с плавающей запятой x ) ( оптимизация ( скорость 3 ) ( отладка 0 ) ( безопасность 1 ))) ( одинарное число с плавающей запятой ( * x Икс )))                  

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

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

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

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

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

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

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

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

 ( defgeneric add ( a b ))   
 ( defmethod add (( число ) ( число b ) ) ( + a b ))        
 ( defmethod add (( вектор ) ( число b )) ( вектор карты ( лямбда ( n ) ( + n b ) ) a ))             
 ( defmethod add (( a вектор ) ( b вектор )) ( карта 'вектор #' + a b ))          
( defmethod add (( строка a ) ( строка b )) ( объединить ' строку a b ))         
 ( добавить 2 3 ) ; возвращает 5 ( добавить #( 1 2 3 4 ) 7 ) ; возвращает #(8 9 10 11) ( добавьте #( 1 2 3 4 ) #( 4 3 2 1 ) ) ; возвращает #(5 5 5 5) ( добавить "COMMON" "LISP" ) ; возвращает «ОБЩИЙ ЛИСП»                        

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

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

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

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

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

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

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

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

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

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

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

Другие типы

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

Объем

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

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

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

Определители объема

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

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

Виды окружающей среды

Глобальный

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

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

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

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

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

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

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

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

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

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

 ( defun foo () ( формат t "Привет, мир" ))     

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

 ( с выводом в строку ( *стандартный вывод* ) ( foo ))  
-> «Привет, мир» ; собранный вывод возвращается в виде строки

Лексический

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

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

 ( defvar *спрятано* ) ;; будет содержать функцию   ( tagbody ( setf *stashed* ( лямбда () ( go some-label ))) ( go end-label ) ;; пропустить (print "Hello") some-label ( print "Hello" ) end-label ) -> НОЛЬ               

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

 ( funcall *спрятано* ) ;; Ошибка!  

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

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

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

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

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

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

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

Макросы

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

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

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

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

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

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

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

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

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

( defmacro Until ( test &body body ) ( let (( start-tag ( gensym "START" )) ( end-tag ( gensym "END" ))) ` ( tagbody , start-tag ( When , test ( go , end- тег )) ( progn ,@ body ) ( go , начальный тег ) , конечный тег )))                      

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

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

( до ( = ( случайное 10 ) 0 ) ( строка записи «Привет» ))      

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 ;; расширение UNTIL широко использует DO ( defmacro до тех пор, пока ( выражение &body body ) ` ( do () ( , выражение ) ,@ body ))          ;; макролет устанавливает привязку лексического оператора для DO ( макролет (( do ( ... ) ... что-то еще ... )) ( до ( = ( случайный 10 ) 0 ) ( строка записи "Hello" )))              

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

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

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

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

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

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

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

Общая объектная система 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- файлы).

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

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

Компилятор файлов вызывается с помощью функции compile-file . Сгенерированный файл с скомпилированным кодом называется файлом fasl (от fast load ). Эти fasl- файлы, а также файлы исходного кода можно загрузить с помощью функции load в работающую систему Common Lisp. В зависимости от реализации компилятор файла генерирует байт-код (например, для виртуальной машины Java ), код языка C (который затем компилируется компилятором C) или, напрямую, собственный код.

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

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

Некоторые специальные операторы предназначены специально для интерактивной разработки; например, defvarприсвоит значение предоставленной переменной только в том случае, если она еще не была привязана, и defparameterвсегда будет выполнять присвоение. Это различие полезно при интерактивной оценке, компиляции и загрузке кода в живом образе.

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

Примеры кода

Парадокс дня рождения

Следующая программа вычисляет наименьшее количество людей в комнате, для которых вероятность уникальных дней рождения меньше 50% ( парадокс дней рождения , где для 1 человека вероятность очевидно 100%, для 2 — 364/365 и т. д. ). Ответ: 23.

В Common Lisp константы по соглашению заключаются в символы +.

( defconstant +размер года+ 365 )  ( defun день рождения-парадокс ( вероятность количества людей ) ( let (( новая вероятность ( * ( / ( - +размер года+ количество людей ) +размер года+ ) вероятность ))) ( if ( < new -вероятность 0,5 ) ( 1+ количество человек ) ( парадокс дня рождения новая вероятность ( 1+ количество человек )))))                      

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

CL-USER > (парадокс дня рождения 1.0 1)23

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

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

( defclass person () (( name :initarg :name :accessor person-name ) ( age :initarg :age :accessor person-age )) ( :documentation "Класс PERSON со слотами NAME и AGE." ))              ( defmethod display (( object person ) stream ) "Отображение объекта PERSON в выходной поток." ( with-slots ( name age ) object ( formatstream "~a (~a)" name age )) )              ( defparameter *group* ( list ( make-instance 'person :name "Bob" :age 33 ) ( make-instance 'person :name "Chris" :age 16 ) ( make-instance 'person :name "Ash" :age 23 )) «Список объектов PERSON». )                     ( dolist ( person ( sort ( copy-list *group* ) #' > :key #' person-age )) ( display person *standard-output* ) ( terpri ))           

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

Боб (33)Ясень (23)Крис (16)

Возведение в степень путем возведения в степень

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

( defun power ( x n ) ( цикл с результатом = 1 while ( plusp n ) if ( нечетный n ) do ( setf result ( * result x )) do ( setf x ( * x x ) n ( truncate n 2 )) наконец ( возврат результата )))                                 

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

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

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

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

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

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

( defun list-matching-lines ( предикат файла ) "Возвращает список строк в файле, для которых предикат, примененный к строке, возвращает T." ( with-open-file ( stream file ) ( цикл для строки = ( read- строковый поток nil nil ) while line When ( строка предиката funcall ) собирает ее )))                       

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

( defun доступные оболочки ( &optional ( file #p"/etc/shells" )) ( файл списка совпадающих строк ( лямбда ( строка ) ( и ( plusp ( длина строки )) ( char= ( строка char 0 ) #\ / ) ( путь ( string-right-trim ' ( #\space #\tab ) строка ))))))                      

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

CL-USER > ( доступные оболочки ) ( #P"/bin/bash" #P"/bin/csh" #P"/bin/ksh" #P"/bin/sh" #P"/bin/tcsh" #P"/bin/zsh" )       

Сравнение с другими Лиспами

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

Common Lisp — это язык программирования общего назначения, в отличие от вариантов Lisp, таких как Emacs Lisp и AutoLISP , которые представляют собой языки расширения, встроенные в определенные продукты (GNU Emacs и AutoCAD соответственно). В отличие от многих более ранних Lisp, Common Lisp (как и Scheme ) по умолчанию использует область лексических переменных как для интерпретируемого, так и для скомпилированного кода.

Большинство систем Lisp, чьи разработки способствовали созданию Common Lisp, например ZetaLisp и Franz Lisp, использовали переменные с динамической областью действия в своих интерпретаторах и переменные с лексической областью видимости в своих компиляторах. Scheme ввел в Lisp единоличное использование переменных с лексической областью; вдохновение от АЛГОЛА 68 . CL также поддерживает переменные с динамической областью действия, но они должны быть явно объявлены как «специальные». Между интерпретаторами и компиляторами ANSI CL нет различий в области видимости.

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

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

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

Реализации

См. раздел Реализации категории Common Lisp .

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

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

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

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

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

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

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

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

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

Вооруженный медведь Common Lisp (ABCL)
Реализация CL, работающая на виртуальной машине Java . [23] Он включает в себя компилятор байт-кода Java и обеспечивает доступ к библиотекам Java из CL. Раньше это был просто компонент редактора Armed Bear J.
Застежка
Реализация на основе 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 использует быстрый компилятор собственного кода. Он доступен в Linux и BSD для Intel x86; Linux для Альфа; macOS для Intel x86 и PowerPC; и Solaris, IRIX и HP-UX на их родных платформах.
Корман Common Lisp
для 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 . GCL еще не полностью совместим с ANSI, но является предпочтительной реализацией для нескольких крупных проектов, включая математические инструменты 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.
МанКай Common Lisp (MKCL)
Филиал ЭКЛ . MKCL подчеркивает надежность, стабильность и общее качество кода благодаря сильно переработанной многопоточной системе времени выполнения. В Linux MKCL имеет полностью совместимую с POSIX систему выполнения.
Мовитц
Реализует среду Lisp для компьютеров x86 , не полагаясь на какую-либо базовую ОС.
Поплог
Poplog реализует версию CL с POP-11 и, при необходимости , Prolog и Standard ML (SML), что позволяет программировать на смешанных языках. В общем, языком реализации является POP-11, который компилируется поэтапно. Он также имеет встроенный редактор, подобный Emacs , который взаимодействует с компилятором.
Общий Lisp Steel Bank (SBCL)
Филиал от CMUCL . «Вообще говоря, SBCL отличается от CMU CL большим упором на удобство обслуживания». [25] SBCL работает на платформах CMUCL, за исключением HP/UX; кроме того, он работает на Linux для AMD64, PowerPC, SPARC, MIPS, Windows x86 [26] и имеет экспериментальную поддержку для работы на Windows AMD64. SBCL по умолчанию не использует интерпретатор; все выражения компилируются в собственный код, если пользователь не включит интерпретатор. Компилятор SBCL генерирует быстрый собственный код в соответствии с предыдущей версией игры The Computer Language Benchmarks Game . [27]
Уфасофт Common Lisp
порт CLISP для платформы Windows с ядром, написанным на C++.

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

Остин Киото Common Lisp
эволюция Kyoto Common Lisp Билла Шелтера
Бабочка Common Lisp
реализация, написанная на Scheme для многопроцессорного компьютера BBN Butterfly [28] [29]
КЛИКК
компилятор Common Lisp в C [30]
КЛОЭ
Common Lisp для ПК от Symbolics
Кодемист Common Lisp
используется для коммерческой версии системы компьютерной алгебры Axiom [31] [32]
ExperCommon Lisp
ранняя реализация для Apple Macintosh от ExperTelligence
Золотой Общий Лисп
реализация для ПК от GoldHill Inc. [33] [34]
Ибуки Common Lisp
коммерческая версия 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 также был доступен. Символика предоставляет расширенную версию Common Lisp. [39] [40] [41]
Процион Общий Лисп
реализация для Windows и Mac OS, используемая Францем для их порта Allegro CL для Windows.
Звездный сапфир Общий LISP
реализация для ПК
СубЛ
вариант Common Lisp, используемый для реализации системы знаний Cyc [42]
Common Lisp верхнего уровня
ранняя реализация для одновременного выполнения [43]
ВКЛ
реализация разделяемой библиотеки [44] [45]
VAX Общий Лисп
Реализация Digital Equipment Corporation , работающая на системах VAX под управлением VMS или ULTRIX.
XLISP
реализация, написанная Дэвидом Бетцем [46]

Приложения

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

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

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

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

Рекомендации

  1. ^ «Действие по стандартам ANSI — 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. ^ «Распространенные реализации Lisp: обзор». Архивировано из оригинала 21 апреля 2012 года . Проверено 22 декабря 2007 г.
  6. ^ «Старые программы LISP все еще работают в Common Lisp» . Проверено 13 мая 2015 г.
  7. ^ "Корни "Ю-Шианг Лисп", письмо от Джона Л. Уайта, 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 . АКМ. стр. 98–107. дои : 10.1145/800068.802140. ISBN 9780897910828. S2CID  14517358.
  11. Редди, Абхишек (22 августа 2008 г.). «Особенности Common Lisp».
  12. ^ «Поддержка Unicode». Вики Common Lisp . Проверено 21 августа 2008 г.
  13. ^ Ричард П. Габриэль; Кент М. Питман (июнь 1988 г.). «Технические проблемы разделения функциональных ячеек и ячеек значений». LISP и символьные вычисления . 1 (1): 81–101. дои : 10.1007/bf01806178. S2CID  26716515.
  14. ^ "Гиперспецификация Common Lisp: Раздел 3.1.7" .
  15. ^ «Гиперспец Common Lisp: функция FLOOR» .
  16. ^ «Гиперспец Common Lisp: аксессуар GETHASH» .
  17. ^ "Отпустить лямбду" . letoverlambda.com .
  18. Питер Сейбель (7 апреля 2005 г.). Практический Common Lisp. Апресс. 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. ^ «История и авторское право». Стил Банк Common Lisp .
  26. ^ "Таблица платформы" . Стил Банк Common Lisp .
  27. ^ «Какие программы самые быстрые? - Игра с тестами компьютерного языка» . 20 мая 2013 г. Архивировано из оригинала 20 мая 2013 г.
  28. ^ «Пакет: lang/lisp/impl/bbn/». cs.cmu.edu .
  29. ^ «Последние разработки в Butterfly Lisp, 1987, AAAI Proceedings» (PDF) . aaai.org . Архивировано (PDF) из оригинала 11 октября 2015 г.
  30. ^ Буркарт, О.; Геригк, В.; Кнутцен, Х. (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 – Общий Lisp для встраиваемых систем». CiteSeerX 10.1.1.2.1953 .  {{cite journal}}: Требуется цитировать журнал |journal=( помощь )
  36. ^ «Концепции программирования TI Explorer» (PDF) .
  37. ^ «Справочник по Лиспу TI Explorer» (PDF) .
  38. ^ «Примечания к выпуску Medley Lisp» (PDF) .
  39. ^ «Символический словарь Common Lisp» (PDF) . Trailing-Edge.com . Архивировано (PDF) из оригинала 22 апреля 2015 г.
  40. ^ «Символика, общие концепции языка Lisp» (PDF) . Trailing-Edge.com . Архивировано (PDF) из оригинала 22 апреля 2015 г.
  41. ^ «Символические конструкции общего программирования на Lisp» (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. ^ «Помощник авторизатора» (PDF) . aaai.org . Архивировано (PDF) из оригинала 4 июня 2011 г.
  49. Помощник авторизатора American Express. Архивировано 12 декабря 2009 г. в Wayback Machine.
  50. ^ Разработка приложений реального времени. Архивировано 2 августа 2016 г. в Wayback Machine . Генсим. Проверено 16 августа 2016 г.
  51. ^ "Genworks GDL" .
  52. ^ "Опусмодус - Дом" .
  53. PWGL – Домашнее архивирование 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. ^ "Лиспинг в Лаборатории реактивного движения" .
  59. ^ «Приложения клиентов Franz Inc: НАСА» . franz.com .
  60. ^ Система планирования и планирования шипов. Stsci.edu. Проверено 17 июля 2013 г.
  61. ^ «Приложения клиентов Franz Inc: Институт космического телескопа» . franz.com .
  62. ^ «Как все началось… АКА рождение CLR» . microsoft.com . 28 августа 2023 г.
  63. ^ Хаффман, Стив. "на шепелявости". Проголосовал за . Архивировано из оригинала 17 мая 2018 года . Проверено 11 мая 2019 г.
  64. ^ "Pgloader".
  65. ^ «Почему pgloader настолько быстрее» . 14 мая 2014 г.

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

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

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