Jump to content

Общий Лисп

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

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

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

Типы данных

[ редактировать ]

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

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

[ редактировать ]

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

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

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

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

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

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

[ редактировать ]

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

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

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

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

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

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

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

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

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

[ редактировать ]

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

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

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

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

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

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

[ редактировать ]

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

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

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

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

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

[ редактировать ]

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

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

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

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

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

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

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

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

Другие типы

[ редактировать ]

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

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

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

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

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

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

[ редактировать ]

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

  • расположение ссылки внутри выражения. Если это крайняя левая позиция составного слова, оно относится к специальному оператору, макросу или привязке функции, в противном случае — к привязке переменной или чему-то еще.
  • вид выражения, в котором имеет место ссылка. Например, (go x) означает передачу управления метке x, тогда как (print x) относится к переменной x. Обе сферы x могут быть активны в той же области текста программы, поскольку метки тегов находятся в отдельном от имен переменных пространстве имен. Специальная форма или макроформа имеет полный контроль над значениями всех символов в своем синтаксисе. Например, в (defclass x (a b) ()), определение класса, (a b) представляет собой список базовых классов, поэтому эти имена ищутся в пространстве имен классов, и x это не ссылка на существующую привязку, а имя нового класса, полученного из a и b. Эти факты вытекают исключительно из семантики defclass. Единственный общий факт об этом выражении заключается в том, что defclass относится к привязке макроса; все остальное зависит defclass.
  • расположение ссылки в тексте программы. Например, если ссылка на переменную x заключен в конструкцию привязки, такую ​​как let который определяет привязку для x, то ссылка находится в области, созданной этой привязкой.
  • для ссылки на переменную независимо от того, был ли символ переменной локально или глобально объявлен специальным. Это определяет, разрешается ли ссылка в лексической среде или в динамической среде.
  • конкретный экземпляр среды, в которой разрешается ссылка. Среда — это словарь времени выполнения, который сопоставляет символы с привязками. Каждый вид ссылки использует свою собственную среду. Ссылки на лексические переменные разрешаются в лексической среде и так далее. С одной и той же ссылкой может быть связано более одной среды. Например, благодаря рекурсии или использованию нескольких потоков одновременно может существовать несколько активаций одной и той же функции. Эти активации используют один и тот же текст программы, но каждая из них имеет свой собственный экземпляр лексической среды.

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

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

[ редактировать ]

Глобальный

[ редактировать ]

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

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

[ редактировать ]

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

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

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

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

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

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

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

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

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

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

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

Лексический

[ редактировать ]

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

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

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

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

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

  (funcall *stashed*) ;; Error!

Эта ситуация ошибочна. Ответом одной реализации является состояние ошибки, содержащее сообщение «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 поддерживает как лексическую, так и динамическую область видимости .

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

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

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

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

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

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

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

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

[ редактировать ]

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

(until test form*)

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

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

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

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

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

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

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

Во время раскрытия макроса значение переменной test равно (= (random 10) 0), а значение тела переменной равно ((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, обработчик условий и пространства имен перезапуска.

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

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

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

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

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

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

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

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

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

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

The 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, которая может обрабатывать и исправлять ошибки без завершения пользовательского кода.

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

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

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

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

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

...the program continues

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

[ редактировать ]

Common Lisp включает в себя набор инструментов для объектно-ориентированного программирования , Common Lisp 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 +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 (цикл чтения Eval Print):

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

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

[ редактировать ]

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

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

(defmethod display ((object person) stream)
  "Displaying a PERSON object to an output stream."
  (with-slots (name age) object
    (format stream "~a (~a)" name age)))

(defparameter *group*
  (list (make-instance 'person :name "Bob"   :age 33)
        (make-instance 'person :name "Chris" :age 16)
        (make-instance 'person :name "Ash"   :age 23))
  "A list of PERSON objects.")

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

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

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

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

[ редактировать ]

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

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

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

CL-USER > (power 2 200)
1606938044258990275541962092341162602522202993782792835301376

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

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

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

[ редактировать ]

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

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

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

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

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

CL-USER > (available-shells)
(#P"/bin/bash" #P"/bin/csh" #P"/bin/ksh" #P"/bin/sh" #P"/bin/tcsh" #P"/bin/zsh")

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

[ редактировать ]

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

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

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

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

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

Библиотеки бесплатного программного обеспечения с открытым исходным кодом были созданы для переносимой поддержки расширений Common Lisp и, в первую очередь, находятся в репозиториях Common-Lisp.net. [20] и CLOCC (коллекция открытого кода Common Lisp) [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)
GNU Компилятор Lisp проекта . 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 и AMD64. [26] 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] и ксерокс [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. ^ «Поддержка Юникод» . Вики 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. ^ «Символические конструкции программирования Common 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. ^ «Генворкс ГДЛ» .
  52. ^ «Опусмодус – Дом» .
  53. PWGL – Домашнее архивирование 3 мая 2011 г., в Wayback Machine , дата обращения 17 июля 2013 г.
  54. ^ Jump up to: а б «Аэрокосмическая промышленность — 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. ^ «Пглоадер» .
  65. ^ «Почему pgloader настолько быстрее» . 14 мая 2014 г.

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

[ редактировать ]

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

[ редактировать ]
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: a38ef06a25f09f62b4db173037797169__1721754540
URL1:https://arc.ask3.ru/arc/aa/a3/69/a38ef06a25f09f62b4db173037797169.html
Заголовок, (Title) документа по адресу, URL1:
Common Lisp - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)