Ceceo | |
---|---|
Semántica | multi- paradigma : programación procedimental , funcional y orientada a objetos |
clase de idioma | lenguaje de programación multiparadigma , lenguaje de programación funcional , lenguaje de programación procedimental , lenguaje de programación reflexivo [d] , lenguaje de metaprogramación [d] y lenguaje de programación interpretado |
Apareció en | 1958 |
Autor | Juan McCarthy |
Desarrollador | John McCarthy [1] y Steve Russell [d] |
sistema de tipos | fuerte , dinámico |
Dialectos |
Lenguajes de extensión Common Lisp ( CLOS ), Scheme , Arc , Clojure : AutoLisp y ELisp |
sido influenciado | Lenguaje de procesamiento de información [d] |
influenciado | Io , Nemerle , Python , Ruby , Smalltalk , Logo , Factor , Perl , Nim |
Archivos multimedia en Wikimedia Commons |
Lisp ( LISP , del inglés LISt Processing language - "lenguaje de procesamiento de listas"; ortografía moderna: Lisp ) es una familia de lenguajes de programación , programas y datos en los que se representan mediante sistemas de listas lineales de caracteres . Lisp fue creado por John McCarthy para trabajar en inteligencia artificial y sigue siendo una de las principales herramientas en esta área. También se utiliza como medio de programación industrial convencional, desde scripts embebidos hasta aplicaciones web de uso masivo, aunque no puede llamarse popular.
Es uno de los lenguajes de programación de alto nivel más antiguos (junto con Fortran y Cobol ) en uso en la actualidad [2] y el primer lenguaje aún en uso que utiliza la gestión automática de memoria y la recolección de basura [3] .
Lisp tradicional tiene un sistema de tipo dinámico . El lenguaje es funcional , pero a partir de las primeras versiones también tiene las características de imperativo , además, al tener instalaciones de procesamiento simbólico completas , permite implementar la orientación a objetos ; un ejemplo de tal implementación es la plataforma CLOS .
Es un lenguaje de programación de sistemas para las llamadas máquinas Lisp producidas en la década de 1980 , por ejemplo, por Symbolics .
Junto con el lenguaje Ada , Lisp pasó por un proceso de estandarización fundamental para uso industrial, dando como resultado el dialecto Common Lisp , que luego fue estandarizado por ANSI . Sus implementaciones existen para la mayoría de las plataformas.
Lisp es un lenguaje que no distingue entre mayúsculas y minúsculas . El alfabeto del idioma se limitó originalmente a la tabla de caracteres ASCII , algunas implementaciones modernas admiten Unicode .
Los elementos básicos del lenguaje son símbolos, átomos y estructuras de listas dinámicas construidas a partir de ellos: expresiones S.
Un símbolo en Lisp es un objeto en la memoria de la máquina, que es una colección de "ranuras": celdas que almacenan referencias. Algunas tragamonedas tienen un propósito originalmente definido por el idioma:
El conjunto de ranuras se puede expandir dinámicamente y, por lo tanto, se puede usar como una lista de propiedades de símbolos arbitrarias (puede expandir libremente el sistema de ranuras de una manera conveniente para resolver el problema). Esta representación nos permite considerar los símbolos como nodos de una red multicoordenada, donde cada coordenada está escrita en su propia ranura.
Los átomos son símbolos y números. Los números no son símbolos Lisp, ya que solo pueden tener su propio valor numérico y ningún otro. Al mismo tiempo, los números, junto con los símbolos, se pueden incluir en las listas. Esta es la razón de combinar estos dos conceptos en una categoría general.
La estructura de datos básica de Lisp es una lista dinámica de átomos, definida recursivamente como un objeto principal y una lista final adjunta. Dado que el encabezado de la lista también puede ser una lista, la lista es una forma de representación de un árbol arbitrario (la lista de nivel superior en sí misma es la raíz, sus sublistas del segundo nivel y subsiguientes son nodos, los átomos son hojas). Para átomos y listas, el lenguaje usa una sintaxis de paréntesis extremadamente primitiva: un símbolo se representa por su nombre, un número se representa por su valor y una lista se representa por una secuencia de listas y átomos encerrados entre paréntesis, con átomos sucesivos separados por espacios si es necesario.
Una lista es una secuencia de elementos de cualquier tipo, incluidas otras listas. Por ejemplo, (1 3/7 'foo #'+)consta de un número entero, una fracción racional, el símbolo foo y un puntero a la función de suma. Las expresiones se representan mediante listas con prefijos : el primer elemento debe ser una forma , es decir, una función , operador , macro u operador especial ; otros elementos son los argumentos de este formulario , pasados al formulario para su procesamiento. Los operadores aritméticos se escriben según el mismo principio, por ejemplo, (+ 4 (* 2 3))da 10 (en notación infija, esto es 2 * 3 + 4).
Sintaxis en forma Backus-Naur :
expresión_s ::= símbolo_atómico | "(" s_expression "." s_expression ")" | lista lista ::= "(" s_expression { s_expression } ")" símbolo_atómico ::= letra parte_átomo atom_part ::= vacío | letra atom_part | número parte_átomo letra ::= "a" | "b" | "..." | "z" número ::= "1" | "2" | "..." | "9" vacío ::= " "Un rasgo característico de un programa Lisp es que absolutamente todo: tanto los datos como el código de cualquier complejidad, se describen en esta sintaxis primitiva. Hay dos resultados de este enfoque:
Cualquier programa Lisp consta de una secuencia de expresiones (formularios). El resultado del programa es evaluar estas expresiones. Todas las expresiones están escritas como listas, una de las principales estructuras de Lisp, por lo que pueden crearse fácilmente dentro del propio lenguaje. Esto te permite crear programas que modifican otros programas o macros , permitiéndote expandir significativamente las capacidades del lenguaje.
Las implementaciones avanzadas de Lisp contienen cientos de funciones del sistema, macros y operadores. Aquí solo se dan aquellos que forman la base para trabajar con listas y crear programas funcionales en Lisp.
T y NIL Los símbolos constantes incorporados de Lisp, que denotan verdadero y falso lógico, respectivamente. Los valores Ty son NILdevueltos por operadores lógicos y operadores y funciones de comparación. Además, el símbolo NILtiene un significado más: puede indicar una lista vacía. CAR y CDR Las funciones devuelven la cabeza y la cola de la lista, respectivamente: (CAR '(A B C D)) ==>> A(en adelante en los ejemplos ==>>significa que como resultado de evaluar el lado izquierdo, el intérprete Lisp produce lo que está a la derecha) (CAR '((A B)(C D))) ==>> (A B) (CDR '(A B C D)) ==>> (B C D) (CDR '((A B)(C D))) ==>> ((C D)) Tenga en cuenta que en el último caso, se devuelve una lista dentro de una lista: la cola del argumento es una lista de un elemento, que a su vez es una lista de dos elementos. Formalmente, en la programación funcional pura, el significado de la cabeza de una lista vacía no está definido, pero Lisp (al menos en la mayoría de los dialectos) tiene la convención de que tanto la cabeza como la cola de una lista vacía son iguales NIL. (CAR NIL) ==>> NIL (CDR NIL) ==>> NIL Las funciones del sistema CARrecibieron CDRnombres tan inusuales por razones históricas. La máquina IBM 704 , en la que McCarthy creó la primera implementación de Lisp, contenía instrucciones para modificar partes de una palabra de máquina. La palabra máquina se consideró como una estructura con campos de prefijo, decremento, etiqueta y dirección. Las funciones CAR(abreviatura de Contenido de la parte de Dirección del Registro ) y CDR( Contenido de la parte de Decremento del Registro ) obtuvieron sus nombres de acuerdo con estos campos [5] . En esta implementación, los campos de dirección y decremento se usaron para almacenar punteros al principio y al final de la lista, respectivamente. En algunas implementaciones nuevas de Lisp, los nombres tradicionales han sido reemplazados por FIRSTy REST(ing. "primero" y "resto", respectivamente) o complementados con estos sinónimos. C*R Aquí, en lugar del asterisco "*" en el nombre de la función, puede haber de 2 a 4 letras "A" y "D" en cualquier combinación. Es decir, las funciones CDDDDR, CADAR, CADDRetc. son posibles. Llamar a una función de este tipo equivale a anidar un conjunto de funciones correspondiente CARy CDR, por ejemplo, (CADAR '((A B C) D E F))coincide (CAR (CDR (CAR '((A B C) D E F))))y devuelve el valor "B". La necesidad de funciones tan extrañas está relacionada con la tarea repetida a menudo: extraer un determinado elemento de la lista, cuya posición se conoce. CONTRAS Toma una cara y una cruz como argumento y crea una lista o un par punteado a partir de ellos si los argumentos son átomos: (CONS 'A '(B C D)) ==>> (A B C D) — unir un átomo a una lista; (CONS '(A B) '((C D))) ==>> ((A B) (C D)) - agregar una lista a la cabeza de otra lista; (CONS 'A 'B) ==>> (A . B) - creación de un par puntual de dos átomos. LISTA Esta función devuelve una lista de sus argumentos: (list 1 3/7 'foo) ==>> (1 3/7 'foo) Si no hay argumentos, se devuelve una lista vacía: (list) ==>> NIL Si algunos elementos son expresiones, entonces su valor se evalúa primero: (list 1 2 (list 1 2)) ==>> (1 2 (1 2)). COTIZAR El operador del sistema QUOTEsuprime la evaluación de su argumento. Si no se usa, el intérprete de Lisp, después de haber recibido una lista o un símbolo en la entrada, intenta calcularlo: para el símbolo, se devuelve su valor, para la lista, el resultado de llamar a la función cuyo nombre está en el cabeza de la lista, con parámetros - la cola de la lista. Si es necesario que el intérprete no calcule valores, sino que tome un símbolo o una lista "tal cual", aplíquelo QUOTE. (LIST 1 2 (QUOTE(LIST 1 2))) ==>> (1 2 (LIST 1 2)) (QUOTE (list 1 2 (list 1 2))) ==>> (LIST 1 2 (LIST 1 2)) Dado que la supresión es una operación muy común, hay azúcar sintáctica que la acorta ; en lugar de la forma completa de la llamada QUOTE, simplemente puede colocar un apóstrofo delante de la expresión: (LIST 1 2 '(LIST 1 2)) ==>> (1 2 (LIST 1 2)). EVALUAR Esta función es, de hecho, el intérprete Lisp. Al ser lo contrario de QUOTE, evalúa el valor de su argumento. (EVAL '(LIST 1 2 '(LIST 1 2))) ==>> (1 2 (LIST 1 2)) (EVAL '(LIST 1 2 (EVAL'(LIST 1 2)))) ==>> (1 2 (1 2)) La capacidad de llamar directa y directamente al intérprete, junto con la identidad de la estructura del programa y los datos, le permite generar y ejecutar directamente cualquier programa Lisp en el sistema sin restricciones. COND Construcción condicional generalizada. Parece: (COND ((Условие1)(Выражение1)) ((Условие2)(Выражение2)) …) Condición1, Condición2 y así sucesivamente se evalúan secuencialmente hasta que la siguiente CondiciónN resulta ser verdadera (comienza a tener el valor T). Luego se ejecutará la ExpressionN correspondiente y su valor se devolverá como el valor de la llamada COND. Si no se encuentra la condición verdadera, CONDdevolverá el valor NIL. Es una práctica común establecer la última condición en COND, Tasegurando así que si fallan todas las demás condiciones, se evaluará la última de las expresiones; así es como se crea un análogo de la rama de ELSEoperadores condicionales de los lenguajes de programación imperativos. DEFUN Una construcción que le permite definir una función. El formato de definición general (simplificado) es el siguiente: (DEFUN Имя (Параметр1 Параметр2 …) Выражение1 Выражение2 …) Aquí Name es el nombre de la función. El símbolo correspondiente, si aún no existe, se creará en el sistema y la definición de función se escribirá en su ranura de función. En el futuro, el intérprete de Lisp, al encontrar el Nombre en la cabeza de la lista calculada, lo interpreta como una llamada a esta función con los parámetros enumerados en la cola. Parámetro1 y así sucesivamente son los nombres de los parámetros formales de la función. La secuencia Expresión1, Expresión2, etc. es una secuencia de expresiones computables que pueden usar Parámetros y variables del sistema global. Cuando se llama a una función, las expresiones se evalúan secuencialmente y el valor de la función se devolverá como el valor evaluado por la última expresión en orden.Los operadores especiales le permiten controlar la secuencia de cálculos. Ramas y bucles se implementan con su ayuda . El operador le permite evaluar una de dos expresiones dependiendo del cumplimiento de una condición, que también es una expresión. Si su resultado no es FALSO (no ), entonces se evalúa el primer argumento, de lo contrario, el segundo. Por ejemplo, siempre devuelve . ifnil(if nil (list 1 2 "foo") (list 3 4 "bar"))(3 4 "bar")
Lisp no requiere, en términos generales, especificar explícitamente los tipos de variables, parámetros y funciones. Pero esto no significa que no haya tipos en Lisp. Lisp usa escritura dinámica , cuando el tipo de datos no se refiere a una variable (parámetro, función), sino a un valor . Esto significa que una variable puede, en ausencia de especificaciones especiales, contener un valor de cualquier tipo de datos, y en diferentes momentos tener valores de diferentes tipos. Los tipos de valor están determinados por cómo se crean esos valores. Por ejemplo, en una expresión (CONS 10 (CONS 2.01 (CONS 'A (CONS "abc" NIL))))en el mismo (primer) parámetro, la misma función ( CONS) recibe secuencialmente un número entero , un número de coma flotante , un átomo y una cadena.
Los sistemas Lisp modernos y avanzados, incluido el Common Lisp estándar, tienen un gran conjunto de tipos de datos incorporados organizados en un sistema jerárquico en el que un tipo puede tener múltiples supertipos y múltiples subtipos. La raíz de la jerarquía es el tipo integrado T, el supertipo de todos los tipos, el tipo final de la jerarquía es el tipo NIL, el subtipo de todos los tipos.
La ilustración de la derecha muestra un fragmento de esta jerarquía: el supertipo para valores de tipos numéricos es NUMBER, sus subtipos - RATIONAL, FLOATy COMPLEX, - representan, respectivamente, un número racional , un número de punto flotante y un número complejo , el primero de que, a su vez, tiene subtipos INTEGER( entero ) y RATIO( fracción racional ), el segundo - varios subtipos que representan números de coma flotante con diversos grados de precisión, y así sucesivamente.
Los tipos creados por el programador también están integrados en este sistema.
Debido a que no todos los tipos de valores son válidos para todas las operaciones, aplicar operaciones a algunos valores genera errores en tiempo de ejecución. El programador puede ignorar esta función (lo que hará que el programa se bloquee si se encuentran datos de un tipo no deseado), usar el manejo de excepciones o verificar si el valor con el que va a trabajar (por ejemplo, pasado a la función como parámetro) pertenece al tipo deseado y, en caso de discrepancia, elija otra forma de resolver el problema o convierta los datos al tipo deseado, cuando sea posible. Para trabajar con tipos, hay: un predicado de verificación de tipos TYPEP, una función de determinación de tipos TYPE-OF, y para la conversión de tipos, si se puede realizar, todo un conjunto de funciones altamente especializadas y una función general COERCE. La verificación de tipos se ve facilitada por el hecho de que el sistema de tipos de Lisp, como se mencionó anteriormente, es jerárquico. Cualquiera de los subtipos es compatible con cualquiera de sus supertipos. Por ejemplo, las operaciones aritméticas se definen para cualquier combinación de cualquier tipo de números, por lo que para la admisibilidad de utilizar, por ejemplo, la suma, basta con comprobar mediante el predicado TYPEPque los operandos son de tipo NUMBER.
( defun SmartPlus ( a b ) ( cond (( and ( typep a 'number ) ( typep b 'number )) ( + a b )) ( t nil )))Así, en el ejemplo anterior, la función SmartPlus devuelve la suma de sus argumentos si son números de cualquier tipo, o nil si al menos uno de ellos no es un número. En este caso, el tipo de retorno específico dependerá de los tipos reales de los parámetros:
( SmartPlus 1 2 ) ==> 3 ( tipo de ( SmartPlus 1 2 )) ==> ( ENTERO 0 4611686018427387903 ) ( SmartPlus 1 1.2 ) ==> 2.2 ( tipo de ( SmartPlus 1 1.2 )) ==> SIMPLE -FLOAT ( SmartPlus 2 2/3 ) ==> 8/3 ( tipo de ( SmartPlus 2 2/3 )) ==> RATIO ( SmartPlus "abc" 20 ) ==> NIL ( tipo de ( SmartPlus "abc " 20 )) ==> NULOEl uso de tipos dinámicos no siempre es conveniente, especialmente cuando se usa un compilador. En primer lugar, la ausencia de restricciones sobre los tipos a los que pueden pertenecer los valores de una variable o parámetro reduce la eficiencia de la compilación. En segundo lugar, no permite de forma estática (durante la compilación, no durante la ejecución del programa) detectar errores relacionados con la violación de la coherencia de tipos. En situaciones donde estos inconvenientes son significativos, Lisp permite utilizar un operador especial para DECLAREespecificar los tipos de parámetros y variables, especificándolos con la precisión necesaria (puede especificar tanto tipos concretos, por ejemplo, RATIOor SHORT-FLOAT, como supertipos de cualquier grado de generalidad, por ejemplo, NUMBER).
Lisp se diseñó originalmente como un lenguaje de programación funcional con ciertas características imperativas introducidas por razones de facilidad de uso práctico. Sin embargo, el formalismo elegido y el conjunto de primitivas en las que se basa el lenguaje permitieron expandirlo en varias direcciones. A lo largo de las décadas de funcionamiento y desarrollo del lenguaje, ha absorbido casi todas las metodologías de programación existentes y actualmente puede considerarse uno de los lenguajes multiparadigmáticos de alto nivel más potentes.
El paradigma funcional es "nativo" de Lisp, ya que la base de su arquitectura es el cálculo lambda de Church . De hecho, fue con Lisp que la programación funcional comenzó como una metodología práctica para el desarrollo de software. Las expresiones lambda son objetos de lenguaje completos en Lisp, que permiten no solo la llamada directa, sino también la asignación, el almacenamiento como un valor de símbolo, el paso como parámetro de llamada y la devolución como resultado. Así, Lisp soporta funcionales, es decir, funciones que toman como parámetros y devuelven como resultado otras funciones.
En diferentes dialectos de Lisp, el enfoque de la naturaleza de las funciones como objetos de lenguaje es algo diferente. En Common Lisp, las funciones y expresiones lambda son una categoría separada de objetos de programa para los cuales existen reglas y restricciones específicas; en particular, esto significa que un símbolo tiene ranuras separadas para el valor y para la función asociada con ese símbolo y, en este sentido, una función no es exactamente lo mismo que un elemento de datos. En otros dialectos, como T-Lisp o Scheme, las funciones se denominan " ciudadanos completos ": pueden asignarse libremente a variables, pasarse como parámetros de llamada y devolverse como resultados de llamada.
El estándar Lisp actual, Common Lisp, ha sido criticado por los partidarios de la programación funcional "pura" porque no toda su funcionalidad es teóricamente "pura". Esto es cierto, ya que Common Lisp fue diseñado como un lenguaje industrial universal y, en varios casos, se dio prioridad deliberadamente a las consideraciones de conveniencia práctica sobre las consideraciones de pureza teórica. Sin embargo, Lisp fue y sigue siendo un lenguaje de programación funcional.
Inicialmente, la sintaxis de Lisp tiene la capacidad de describir algoritmos enumerando la secuencia de acciones requeridas. Se encuentra en el llamado "PROGN implícito" soportado en la estructura de las llamadas lambda de Lisp: en el lugar donde debe colocarse el comando que forma la base de la expresión lambda, no se puede escribir uno, sino varios comandos, y el El resultado de la llamada lambda será el resultado del último de ellos. Por lo tanto, Lisp admite la ejecución secuencial implícita de operaciones. Además de PROGN implícito, se admiten mecanismos imperativos explícitos para programación imperativa:
Common Lisp tiene un lugar especial en la macro del sistema LOOP. Le permite crear un fragmento en un programa Lisp escrito en un lenguaje de programación con la estructura imperativa habitual y la notación infija de operadores.
La función de programación de macros más simple disponible en cualquier implementación de Lisp es la capacidad de llamar a un intérprete (la función EVAL ) directamente, pasándole cualquier estructura de lista como un programa. Esto permite que el programa de nivel superior actúe como un macrogenerador, es decir, generar código ejecutable que se ejecutará posteriormente. Por ejemplo:
( defun nombre ( nombre lv ) ( eval ( cons 'defun ( cons nombre ( cdr lv ))))) ( nombre 'add ' ( lambda ( x y ) ( + x y ))) ( suma 5 7 ) ==>12Aquí, la función назватьconstruye una nueva definición de función a partir del nombre que se le pasó y una expresión lambda, y luego ejecuta esta definición con eval. Como resultado, сложитьaparece una nueva función (en este ejemplo, función ) en el sistema y se puede llamar de la forma habitual.
Sin embargo, esta característica rara vez se usa debido a su engorroso. El sistema de macros se usa mucho más comúnmente . Las variantes modernas de Lisp, incluido el estándar Common Lisp, tienen funciones avanzadas para crear y usar macros . Una declaración de macro en Lisp es sintácticamente similar a una declaración de función (la única diferencia es el uso de la palabra clave defmacro en lugar de defun en la declaración), pero el comportamiento de la macro es significativamente diferente: cada llamada a la macro se "expande" en el momento en que se traduce el programa, generando código que, en tiempo de ejecución, se ejecuta de la siguiente manera: como si estuviera escrito directamente en el sitio de la llamada. Otra diferencia entre las macros y las funciones ordinarias es que sus argumentos no se evalúan por defecto. La implementación del ejemplo anterior como una macro podría verse así:
( defmacro nombre ( nombre lv ) ( cons 'defun ( cons nombre ( cdr lv )))) ( nombre add ( lambda ( x y ) ( + x y ))) ( suma 12 8 ) ==>20Hay dos diferencias visibles: no hay llamada en la definición de macro eval, y los apóstrofes antes de los parámetros no se usan en su llamada, ya que los parámetros de macro no se evalúan. Pero otra diferencia es mucho más importante: si en el primer ejemplo la construcción de una nueva función ocurre durante la ejecución del programa, entonces la expansión de la macro se realiza durante la traducción del programa, para que el compilador tenga la oportunidad de procesar la función сложить. Los errores de sintaxis en la formación de funciones en macros también se detectan no durante la ejecución del programa, sino incluso en la etapa de traducción.
Además, varias implementaciones de Lisp admiten la creación de las llamadas "macros de lectura", macros que transforman directamente el texto del programa. Las macros le permiten definir de forma transparente nuevas construcciones de lenguaje e incluso ampliar la sintaxis del lenguaje. La última posibilidad se usa activamente cuando se implementan en Lisp métodos y herramientas de programación que inicialmente no son compatibles con él.
Lisp no fue diseñado como un lenguaje orientado a objetos. El propio paradigma de programación orientada a objetos se desarrolló una década y media más tarde que Lisp, sin embargo, cuando apareció y se hizo popular, se agregaron capacidades de objetos a este lenguaje.
El conjunto de funciones básicas de Lisp hace que agregar un subsistema orientado a objetos a Lisp no solo sea posible, sino también fácil. Debido a la presencia de propiedades (ranuras) para los símbolos, Lisp admite de forma nativa la encapsulación . Las propiedades funcionales de Lisp (soporte para funcionales, asignación de funciones a variables y almacenamiento en propiedades de símbolos) hacen posible asociar código (métodos) con datos (objetos). Finalmente, la naturaleza dinámica del lenguaje, combinada con las características anteriores, asegura el comportamiento polimórfico del código y los datos en un programa. El único componente de un sistema OOP que no se encuentra en Lisp base es la herencia, pero se puede implementar sin dificultad. Así, Lisp contiene todos los elementos en los que se basa la tecnología OOP, y la implementación de su soporte en el lenguaje se reduce a la creación de los elementos sintácticos correspondientes. Gracias al mecanismo de macros desarrollado, se pueden añadir utilizando el propio lenguaje, sin necesidad de ampliar la sintaxis básica y modificar compiladores. Un ejemplo simple y elegante de cómo crear su propio subsistema OOP en Lisp se puede encontrar en ANSI Common Lisp [6] de Paul Graham .
Entre las extensiones orientadas a objetos de Lisp comercialmente conocidas, debe mencionarse en primer lugar el subsistema de objetos Flavours, que se incluyó en el sistema Zetalysp. Este subsistema proporcionó declaraciones de clase (sabores), herencia única y múltiple, métodos de clase polimórficos, un sistema de interacción de objetos similar a Smaltalk mediante el paso de mensajes (implementado como una llamada de método de objeto). Otro ejemplo es LOOPS (Sistema de Programación Orientado a Objetos Lisp), un subsistema de objetos implementado en 1983 en el dialecto InterLisp.
El sistema de objetos CLOS (Common Lisp Object System), creado originalmente además de Common Lisp y luego incluido en el estándar del lenguaje, es similar a Flavors y admite esencialmente el mismo conjunto de características que es estándar para casi cualquier lenguaje moderno orientado a objetos. . El uso de la programación orientada a objetos en Lisp se asocia principalmente a la resolución de problemas de modelado y/o control, que por su naturaleza se combinan con éxito con la tecnología orientada a objetos. Por ejemplo, una de las primeras aplicaciones del sistema Flavours fue interactuar con una interfaz de usuario de múltiples ventanas, que es perfecta para modelar como un conjunto de objetos de mensajería.
El autor de Lisp es John McCarthy , quien en el momento de la creación del lenguaje trabajaba en el Instituto Tecnológico de Massachusetts (MIT) como profesor de comunicaciones. Junto con Marvin Minsky , se dedicó al trabajo sobre inteligencia artificial, en relación con lo cual surgió la necesidad de crear un lenguaje de programación adecuado a las tareas que se resolvían en esta área. El trabajo sobre la creación de lenguajes fue realizado por McCarthy en el MIT entre 1958 y 1963 , después de lo cual se trasladó a la Universidad de Stanford en California, donde recibió el puesto de "profesor de inteligencia artificial".
Lisp se basó en el lenguaje IPL temprano desarrollado por Newell, Shaw y Simon. IPL era un lenguaje de procesamiento de listas y estaba destinado a implementar el proyecto Logic Theorist, un sistema de inteligencia artificial diseñado para derivar automáticamente teoremas de lógica matemática. IPL era un lenguaje de nivel bastante bajo, pero ya implementaba ideas tan básicas como un mecanismo único para almacenar programas y datos en forma de listas: estructuras jerárquicas de elementos vinculados por enlaces (la idea misma de una lista de representación del conocimiento fue tomado de la investigación en psicología y memoria asociativa), así como la idea de asignación de memoria dinámica. Después de familiarizarse con IPL en 1956, McCarthy tuvo la idea de implementar el procesamiento de listas de IPL en Fortran , que se estaba diseñando en IBM en ese momento (y bajo el mismo sistema IBM 704 con el que McCarthy trabajaba en el MIT), pero esta idea tiene no ha sido implementado. McCarthy participó más tarde en el trabajo del "comité lingüístico de alto nivel" que desarrolló Algol , pero incluso allí sus propuestas fueron recibidas con frialdad. Como resultado, a McCarthy se le ocurrió la idea de crear un nuevo lenguaje de programación.
Inicialmente, McCarthy formuló un formalismo de lista para describir datos ( expresiones S ) y un mecanismo para describir expresiones lambda basado en él , lo que hizo posible escribir programas como conjuntos de funciones presentadas en forma de lista. Como McCarthy escribió más tarde, originalmente planeó usar un formalismo separado para escribir programas, diferente de las expresiones S, pero resultó ser redundante. Cuando, utilizando su notación de lista, McCarthy describió cómo funcionaría el intérprete de un nuevo idioma (un formalismo que se conocería como "Lisp on Lisp"), Steve Russell notó que ahora, para crear un intérprete real que funcione, es suficiente simplemente para traducir esta notación a código de máquina. McCarthy se mostró escéptico acerca de esta idea, pero Russell realmente hizo este trabajo y obtuvo el primer intérprete de Lisp para la computadora IBM 704. Más tarde, la idea de escribir un traductor de idiomas en el propio Lisp se usó repetidamente, y no solo en términos funcionales y lenguajes lógicos, pero también en los imperativos.
Históricamente, la primera implementación de Lisp que incluyó todos los elementos básicos modernos del lenguaje fue un intérprete que se ejecutaba en IBM 704, que apareció en octubre de 1958. Esto, por cierto, nos permite hablar de Lisp como uno de los dos lenguajes de alto nivel más antiguos que han estado en uso desde el momento de su creación hasta la actualidad (el primero es Fortran). Además, Lisp mantuvo su liderazgo en otro aspecto. El hecho es que el trabajo activo con listas dinámicas hizo imposible administrar manualmente la memoria, que se conserva en parte en los idiomas imperativos hasta el día de hoy. La creación de nuevas celdas y listas de listas y la salida del uso de los programas Lisp existentes son tan activas que es casi imposible prescindir de un sistema automático de gestión de memoria que controle el uso de objetos creados previamente en la memoria y borre periódicamente los de los que han dejado de utilizarse, es decir, los sistemas de recogida de basura . McCarthy tuvo que implementar este sistema, haciendo de Lisp, entre otras cosas, también el lenguaje de programación más antiguo en uso hoy en día con gestión automática de memoria y recolección de basura.
Más tarde, se crearon implementaciones para IBM 7090, y más tarde para las series IBM 360 y 370. Las computadoras IBM resultaron ser un inconveniente para trabajar en modo interactivo, como resultado de lo cual, a fines de la década de 1950, un pequeño grupo de desarrolladores, incluidos los que habían trabajado anteriormente en IBM, separados en la empresa independiente Digital Equipment Corporation (DEC). Su primer producto fue la computadora PDP-1 , originalmente enfocada en el modo interactivo de operación. En esta máquina, en 1960, se implementó el sistema interactivo Lisp 1, que incluye un intérprete integrado, un editor de código fuente y un depurador, lo que permitió realizar todo el ciclo de trabajo del programa directamente en el sistema. De hecho, fue el primer "entorno de programación" en el sentido que ahora se le está dando a este concepto. Al mismo tiempo, el artículo de McCarthy "Recursive Functions of Symbolic Expressions and their Computation by Machine" se publicó en la revista Communications of ACM, en el que Lisp se describía como un formalismo algebraico sobre Lisp mismo. El artículo se convirtió en un clásico, y el formalismo "Lisp on Lisp" se ha convertido desde entonces en uno de los más utilizados en la literatura sobre teoría de la programación. Otra innovación tecnológica que apareció en relación con la implementación del sistema "Lisp 1" fue el mecanismo inventado por McCarthy, que permitió que el intérprete de Lisp se ejecutara simultáneamente con la ejecución del trabajo computacional normal en modo por lotes (lo que ahora se conoce como el " sistema de tiempo compartido").
En 1962, estaba lista la siguiente versión del sistema Lisp original "Lisp 1.5", en la que se eliminaron las deficiencias de la primera versión descubiertas durante la operación. Su descripción fue publicada por MIT Press como un libro separado [7] . Debido a que el manual incluía una descripción de la implementación del sistema, se convirtió en la base para la creación de sistemas Lisp para muchas otras computadoras tanto en los EE. UU. como en el extranjero.
A pesar del uso, a veces bastante activo, de Lisp en países europeos y asiáticos y la creación de sus propios sistemas Lisp allí, la mayoría de los dialectos comunes de Lisp provienen de los Estados Unidos.
Mac LispA principios de la década de 1960, se lanzó el proyecto MAC en el MIT, bajo el cual se desarrolló MacLisp basado en Lisp 1.5, dirigido principalmente a computadoras PDP. MacLisp era extremadamente poderoso para su época, con una alta eficiencia computacional y una amplia gama de tipos de datos matemáticos, incluidos vectores, matrices y campos de bits. Como parte de la contribución al desarrollo del lenguaje mismo, se pueden notar las macros de lectura y las tablas de lectura que aparecieron en MacLisp, que permitieron “terminar” el lenguaje, ampliándolo en la dirección correcta con nuevas estructuras. El manejo de excepciones y las facilidades de procesamiento paralelo también se incluyeron en el lenguaje. MacLisp también se convirtió en el primer sistema Lisp para el cual se implementó un compilador altamente eficiente .
El sistema de álgebra computacional Macsyma fue escrito completamente en MacLisp , cuyo desarrollo se inició como parte del proyecto MAC en 1968. Macsyma siguió siendo el sistema más avanzado de su tipo durante muchos años, con varios dialectos de Lisp creados específicamente para trasladar Macsyma a otras plataformas. Otra pieza de software muy famosa y todavía en uso desarrollada originalmente en MacLisp es el editor de texto de pantalla completa Emacs .
El sistema MacLisp fue explotado y desarrollado hasta la década de 1980, lo que tuvo un impacto significativo en las implementaciones de Lisp que aparecieron en las décadas de 1960 y 1980, incluso convirtiéndose en una de las fuentes para el diseño del estándar Common Lisp. El funcionamiento del sistema prácticamente cesó en la década de 1980, junto con el cese del uso de los ordenadores PDP-10/20 en los que se basaba originalmente. Los sistemas desarrollados sobre MacLisp y los ya mencionados Macsyma y Emacs han sobrevivido mucho .
InterlispMuchas empresas y centros de investigación de los Estados Unidos participaron en el desarrollo de los sistemas Lisp a mediados de la década de 1960. Interlisp fue el resultado de los esfuerzos combinados de BBN (Bolt, Beranek and Newman Inc.), SDS (Scientific Data Systems) y Xerox. El Centro de Investigación BBN en 1966 comenzó a crear su implementación de Lisp, enfocada en las computadoras PDP-10 y SDS-930. La versión PDP de BBN-Lisp utilizó un mecanismo de cambio de contexto y paginación de hardware diseñado específicamente para proporcionar un tiempo compartido altamente eficiente. BBN-Lisp se hizo popular entre los investigadores de IA y contribuyó en gran medida al hecho de que las máquinas PDP-10/20 siguieran siendo las principales herramientas en el trabajo de IA hasta la década de 1980. A principios de la década de 1970, Xerox Corporation compró SDS en bancarrota y se asoció con BBN. Aunque las máquinas SDS no tuvieron mucho éxito comercial, la implementación de Lisp por parte de BBN fue lo suficientemente prometedora como para que Xerox respaldara su desarrollo posterior, lo que resultó en que BBN-Lisp se convirtiera en Interlisp .
Y en 1974, Xerox comenzó a desarrollar la estación de trabajo personal Alto, originalmente orientada hacia Lisp. En este sistema se realizó por primera vez el desarrollo de un equipo y un sistema de instrucciones máquina para un lenguaje de programación específico. Sobre la base de Interlisp, se creó una versión simplificada del sistema Interlisp-D, diseñada para las máquinas Lisp de la serie 1100 (los "descendientes" de la estación Alto). Estas máquinas fueron las primeras en implementar una interfaz gráfica de usuario de múltiples ventanas, usar gráficos de alta resolución y usar el mouse.
El sistema estaba ampliamente documentado e incluía un IDE bien diseñado con un editor de código fuente, un depurador, un intérprete y muchas ayudas para desarrolladores, convirtiéndose en uno de los entornos de programación ejemplares para los sistemas de tiempo compartido. Se implementaron más de 500 funciones en las bibliotecas del sistema, el sistema contaba con una gran cantidad de configuraciones que permitían “a la medida” del usuario. Las implementaciones de Interlisp finalmente se ejecutaron en las grandes computadoras de tiempo compartido más utilizadas.
En cuanto al idioma en sí, se puede señalar que el dialecto con todos sus rasgos característicos ya se fijó a mediados de la década de 1970, después de lo cual no se realizaron cambios fundamentales en el idioma. Esto provocó que el sistema se retrasara con respecto a los nuevos desarrollos en términos de funcionalidad y corrigiera algunas decisiones de diseño obsoletas. Como resultado, a principios de la década de 1980, Interlisp estaba experimentando dificultades tanto con la compatibilidad con los nuevos sistemas como con una mayor expansión. Las deficiencias más significativas son la falta de una jerarquía de tipos de datos, objetos y cierres (sin embargo, en 1983 se implementó el sistema de objetos LOOPS, que da la posibilidad de programación orientada a objetos). Más importante aún, InterLisp se basa en enlaces dinámicos, mientras que todas las nuevas versiones de Lisp son estáticas.
PSLLisp llegó a California con McCarthy, quien se mudó a Stanford en 1963. Durante los años siguientes, se desarrollaron los sistemas Lisp 1.6 (un descendiente directo del "clásico" Lisp 1.5), UCI Lisp (Universidad de California, Irvine) y Stanford Lisp/360. De allí, junto con Anthony Hearn, Lisp pasó a la Universidad de Utah , donde investigó en el campo de las matemáticas simbólicas en aplicaciones de la física teórica. Hearn propuso resolver estos problemas usando Lisp, lo que resultó en la creación del sistema de álgebra computacional Reduce en 1968 .
Hearn publicó la especificación Standard Lisp en 1966, que propuso como base para estandarizar el lenguaje. Su propuesta no encontró apoyo, ya que no fue aprobada por los investigadores de inteligencia artificial, quienes señalaron una serie de características del estándar propuesto que no eran deseables para ellos, en particular, la vinculación excesiva a los tipos. Sin embargo, en base a esta especificación, Portable Standard Lisp, PSL, se implementó en Utah. Esta implementación se utilizó para desarrollar Reduce y migrarlo a varias plataformas de hardware. Específicamente para mejorar la portabilidad, se ha incluido en PSL un conjunto reducido de funciones y estructuras del sistema. La implementación se basó en un lenguaje similar a Lisp de bajo nivel intermedio, SYSLisp; el núcleo de PSL se escribió en SYSLisp y el resto del sistema se escribió en PSL mismo. Para el PDP-10, se implementaron el traductor SYSLisp y un compilador cruzado escrito en el mismo SYSLisp, con la ayuda del cual el núcleo PSL podría transferirse a cualquier otro hardware. Usando esta tecnología, PSL y Reduce se han implementado en una variedad de plataformas, incluidas DEC-10/20, VAX/UNIX, HP9000, Apollo, Wicat, IBM, Cray.
Así, PSL se convirtió en uno de los primeros ejemplos de la implementación de la técnica “spin-up” al portar sistemas de software a una nueva arquitectura, cuando, para portar el sistema, el kernel se escribe inicialmente en un lenguaje intermedio independiente de la máquina. , para lo cual, a su vez, se crean implementaciones en todas las plataformas de destino. El centro de investigación de Hewlett-Packard en California llevó a cabo más apoyo para PSL .
Franz LispLa motivación de Franz Lisp a fines de la década de 1970 fue obtener un sistema Lisp para que las nuevas computadoras VAX ejecutaran el sistema Macsyma y otro software escrito por Lisp. Dado que el objetivo principal era portar Macsyma, se tomó como base MACLisp, sin embargo, se excluyeron algunas características obsoletas del lenguaje y se agregaron nuevos mecanismos, tomados de Zetalisp que se estaba desarrollando en ese momento en el mismo MIT. Las contribuciones más significativas a la creación de este dialecto fueron realizadas por la Universidad de Berkeley , la Universidad de Pensilvania , Bell Labs , el Laboratorio Nacional de Livermore y la Universidad Carnegie Mellon . Una de las principales inspiraciones para el proyecto fue el profesor de la Universidad de Berkeley, Richard Feitman, anteriormente del MIT e involucrado en el desarrollo del sistema Macsyma original. Entre los creadores de Franz Lisp se encontraban varios de sus alumnos. El nombre del sistema fue elegido en honor al famoso compositor húngaro Franz Liszt (ortografía en inglés: Franz Liszt).
El sistema se implementó en 1981 en C para VAX 780/11 con UNIX . El compilador incluido en el sistema se llamó "Liszt", el apellido del compositor que le dio el nombre al dialecto. En 1982, el sistema fue portado al procesador Motorola 68000 , luego a varias plataformas personales de 32 bits, como resultado se convirtió en la versión más utilizada de Lisp tanto para sistemas de tiempo compartido de 32 bits como para minicomputadoras de 32 bits. y estaciones de trabajo personales.
Franz Lisp se distribuyó de forma gratuita bajo la licencia BSD, pero al estudiante graduado de Berkeley, Friedrich Kunze, se le ocurrió la idea de crear una empresa comercial que brindara soporte de usuario pago de alta calidad y cumpliera con los pedidos de Franz Lisp portando a nuevo hardware y plataformas de software Era una época de crecimiento activo en el mercado de las computadoras y las perspectivas parecían buenas. La empresa se registró en 1984 y se llamó Franz Inc. El inicio de las actividades de la compañía fue bastante exitoso, logró obtener un contrato para transferir Franz Lisp a la plataforma Sun y, más tarde, varias ofertas similares más. Sin embargo, en 1985, bajo la presión del Departamento de Defensa de EE. UU., la comunidad estadounidense de Lisp comenzó una reorientación activa hacia un nuevo dialecto: Common Lisp, que se estaba completando en ese momento. Bajo estas condiciones, Franz Inc. no pudo encontrar nuevos contratos, estuvo a punto de cerrar y se vio obligado a pasar a desarrollar su propia implementación de Common Lisp - Allegro Common Lisp (el nombre fue elegido para preservar la continuidad del tema "musical"). La historia de Franz Lisp en realidad terminó aquí. El sistema original ahora está completamente fuera de uso.
EsquemaEl lenguaje Scheme se desarrolló en 1976 en el MIT como parte del proyecto Lisp Machine, una estación de trabajo personal diseñada completamente a partir de hardware para aprovechar al máximo el lenguaje Lisp. Inicialmente, Scheme era solo un "lenguaje de investigación", durante el desarrollo del cual se probaron varias ideas y métodos. El objetivo era implementar un conjunto mínimo de características básicas que aseguraran la construcción de un sistema Lisp completo basado en este conjunto.
El resultado es un kernel pequeño y elegantemente definido, pero implementado de manera muy efectiva. En particular, Scheme fue el primer dialecto de Lisp en garantizar la optimización de recurrencia de cola. El lenguaje implementa un poderoso mecanismo de macros, además de las listas, las matrices son compatibles como construcciones básicas. Una diferencia sintáctica característica entre Scheme y la mayoría de los dialectos Lisp es una forma ligeramente diferente de definición de función. Si en la mayoría de los dialectos se usa la variante: (DEFUN ИмяФункции (Аргументы) Выражения), entonces en Scheme la forma abreviada de la definición se parece a (DEFINE (ИмяФункции Аргументы) (Выражения)). (La palabra clave y la posición relativa del nombre de la función y los argumentos difieren). Scheme utiliza enlaces dinámicos e implementa un único espacio de nombres para funciones y variables, lo que lo distingue de Common Lisp.
Scheme es el único dialecto "antiguo" de Lisp que continúa usándose después de la transición generalizada de la comunidad Lisp al Common Lisp estandarizado. Actualmente, hay varias implementaciones compatibles de Scheme, incluidas las gratuitas, hay ejemplos del uso de este lenguaje como uno integrado (por ejemplo, se usa como una herramienta de secuencias de comandos GIMP Tiny-Scheme). Varias universidades estadounidenses utilizan Scheme como lenguaje para la enseñanza de la programación básica.
ZetalispZetalisp o "Lisp Machine Lisp" fue creado en el MIT en la segunda mitad de la década de 1970 como parte del Proyecto Lisp Machine financiado por la agencia de defensa estadounidense DARPA.
El sistema se basó en MacLisp y el editor Emacs, pero el lenguaje se actualizó y complementó significativamente, en particular, aparecieron nuevos tipos de datos, el subsistema orientado a objetos Flavours, en el que la interacción de programas con una interfaz de usuario de múltiples ventanas se basa, nuevas estructuras de control de directivas, parcialmente prestadas de Interlisp, funciones de múltiples valores (capaces de devolver más de un valor de manera regular sin "ensamblarlos" primero en un contenedor), transmisión de E / S, espacios de nombres, una biblioteca poderosa de funciones, incluidas las matemáticas, proporcionando cálculos vectoriales y matriciales y trabajando con sistemas lineales.
Se hizo mucha más innovación en el propio sistema de programación. El sistema fue originalmente diseñado para trabajar con una terminal gráfica de usuario y un mouse. Implementó una interfaz gráfica de usuario de múltiples ventanas. El sistema incluía un intérprete Lisp de múltiples ventanas, un traductor parcial, un editor de texto Zmacs, un inspector de estructura de datos, un depurador, un explorador de estado del sistema, un editor de archivos del sistema, un editor de fuentes y un cliente de correo electrónico Zmail. El sistema incluía traductores de otros lenguajes de alto nivel, un convertidor que brindaba soporte para los programas de Interlisp y un conjunto de herramientas de alto nivel. Fortran, Pascal, Ada y Prolog, que se suministraron como parte del sistema, se desarrollaron medios de interacción con los programas Lisp, lo que hizo posible, si es necesario, desarrollar y aplicar sistemas de software en varios idiomas.
Inicialmente, el proyecto tenía como objetivo crear un producto comercial. En 1979, se crearon dos empresas: fabricantes de máquinas Lisp: Symbolics y Lisp Machine Inc. (LMI). Después de eso, el trabajo en el desarrollo de Zetalisp fue llevado a cabo por estas empresas de forma independiente. Sin embargo, aunque había algunas diferencias en las propias máquinas Lisp, en términos de lenguaje eran casi completamente compatibles.
NIL y TLa implementación de MACLisp en la máquina VAX en el propio MIT comenzó en 1979. El proyecto se denominó NIL (simultáneamente, la abreviatura "Nueva implementación de Lisp" - "Nueva implementación de Lisp" - y el átomo estándar de Lisp "NIL", que denota, según el uso, no verdad lógica o una lista vacía). NIL tenía un kernel bastante grande, escrito en ensamblador VAX, sobre el cual se construyó el sistema Lisp utilizando el mismo método de desenrollado. Hasta cierto punto, NIL puede considerarse "la respuesta a Franz Lisp", ya que uno de los objetivos del proyecto era la misma migración del sistema Macsyma a VAX. NIL tomó prestado mucho de Zetalisp, incluido el sistema Flavors, que convierte el sistema Lisp en uno orientado a objetos. En 1981, el grupo involucrado en el proyecto NIL se disolvió debido a diferencias irreconciliables en cuanto a la ideología del sistema que se estaba creando. A pesar del colapso, desde 1982, se han publicado actualizaciones periódicas del sistema y ha recibido una distribución bastante notable. En la década de 1980, NIL se usaba a menudo en organizaciones que tenían máquinas VAX y Lisp, ya que no existen diferencias ideológicas fundamentales entre NIL y Zetalisp, aunque Zetalisp es mucho más rico en funciones.
Un grupo de desarrolladores que se separó del proyecto NIL comenzó a crear su propia versión del sistema Lisp, que recibió el nombre irónico " T " (simultáneamente - de "True Lisp" - "Real (true) Lisp" y otro átomo estándar de Lisp "T", que denota la verdad lógica, es decir, lo contrario de "NIL"). El desarrollo de este dialecto se llevó a cabo en la Universidad de Yale en 1982-1984. A diferencia de los sistemas "antiguos", el dialecto T usaba el enlace de variables estáticas por defecto, además, sus creadores introdujeron la implementación de funciones como "ciudadanos completos", lo que significa que las funciones se pueden asignar a las variables sin medios sintácticos especiales y sin restricciones y devueltos como valores de otras funciones. T-Lisp, a diferencia de NIL, tenía un kernel bastante pequeño escrito en lenguaje de máquina. Los desarrolladores utilizaron la técnica "spin-up", trasladando manualmente el kernel a nuevas plataformas e implementando el resto del sistema directamente en Lisp, con la expectativa de que un traductor altamente eficiente proporcionaría un mejor rendimiento para el sistema final que implementar manualmente una máquina grande. núcleo del lenguaje.
Un punto controvertido en T-Lisp fue la decisión de los autores de actualizar y sistematizar los nombres de las funciones del sistema. Entonces, por ejemplo, los nombres de todos los predicados sin excepción terminaron en un signo de interrogación, los nombres estándar "establecidos históricamente" de funciones elementales fueron reemplazados por nombres mnemotécnicos, correspondientes a lo que hace la función. Por ejemplo, las funciones CARy CDR, que devuelven el principio y el final de la lista, respectivamente, se denominan FIRSTand REST(ing. "primero" y "resto"). La ventaja indudable de esta decisión fue la facilitación del aprendizaje, la desventaja obvia fue la incompatibilidad con todos los demás dialectos del idioma. Como resultado, los creadores aún tuvieron que complementar posteriormente el sistema con un conjunto de macros que alinean el sistema de nombres con el estándar Common Lisp. Ciertamente, una influencia significativa que el dialecto Scheme ha tenido en T. En general, T-Lisp resultó ser un sistema bastante simple, elegante y móvil que se implementó para VAX y se transfirió a muchas estaciones de trabajo de 32 bits.
Para la primera mitad de la década de 1980, se había desarrollado una situación en la comunidad Lisp que algunos autores compararon con la Torre de Babel : más de una docena de grandes dialectos de Lisp existían y se desarrollaban en paralelo, mientras que el número total de implementaciones eran incompatibles entre sí. fue significativamente mayor. Una situación similar se observó en ese momento en la mayoría de los lenguajes de programación comunes, pero en el caso de Lisp, la situación se vio agravada por el hecho de que el lenguaje fue diseñado originalmente como arbitrariamente extensible, lo que provocó el desarrollo de sus capacidades en diferentes dialectos en forma significativa. direcciones diferentes. Si en la etapa inicial, cuando Lisp se usaba casi exclusivamente en laboratorios e institutos, la variedad de dialectos no interfería particularmente e incluso era algo útil, ya que contribuía al rápido desarrollo del lenguaje, luego en la década de 1980, cuando hubo una necesidad de desarrollo industrial en Lisp, la abundancia de implementaciones se convirtió en un freno, porque condujo a una duplicación masiva del desarrollo y la dispersión de fuerzas para soportar muchos sistemas Lisp.
Se han realizado intentos de estandarizar Lisp casi desde sus inicios (la primera propuesta de estandarización se remonta a 1960), pero debido a la desunión y diferencias significativas en las necesidades de los grupos de desarrollo interesados, ninguna de las propuestas fue aceptada. En la segunda mitad de la década de 1970, el Departamento de Defensa de EE. UU. hizo un gran trabajo al analizar la situación en el desarrollo de software militar, después de lo cual organizó una competencia para desarrollar un nuevo lenguaje de alto nivel para sistemas integrados, que se convirtió en el lenguaje Ada . Sin embargo, Ada no estaba originalmente destinado a la inteligencia artificial y el procesamiento de caracteres, como resultado de lo cual el ejército de los EE. UU. se vio obligado a permitir el uso de un lenguaje más adecuado para tales desarrollos. Por lo tanto, el Departamento de Defensa de EE. UU. brindó apoyo organizacional y financiero para la formación de un estándar industrial para el lenguaje Lisp, que adoptó como una herramienta adicional para desarrollar software para aplicaciones militares.
El borrador original del estándar comenzó en la Universidad Carnegie Mellon sobre la base de un proyecto interno de Spice Lisp, también destinado originalmente a desarrollar un sistema Lisp para la estación de trabajo. Desde el principio, el estándar proyectado recibió el nombre de "Common Lisp" ("Common Lisp"), enfatizando el objetivo de desarrollo: obtener un lenguaje base único, sobre la base del cual sería posible crear sistemas compatibles con software. Alrededor de 80 especialistas de universidades, laboratorios y empresas estadounidenses participaron en el desarrollo y edición de la norma. Por primera vez, el proceso de desarrollo se llevó a cabo de forma remota, a través de la red informática ARPANET , a través de la cual se transmitieron más de 3.000 mensajes. El proceso de desarrollo estándar finalizó en 1984. Su resultado se registró en la primera edición de Common Lisp: the Language de Guy Steele.
El advenimiento de Common Lisp ralentizó la creación de nuevos dialectos del idioma. Los dialectos "antiguos" continuaron existiendo, pero a medida que las plataformas en las que se ejecutaban cayeron en desuso, también lo hicieron los sistemas Lisp correspondientes. La mayoría de ellos dejó de existir en 1985-1995. Ya se hicieron nuevos desarrollos en Common Lisp. Sin embargo, en los años siguientes aparecieron varios dialectos nuevos de Lisp, la mayoría de los cuales siguieron el camino de la simplificación y estaban orientados hacia las microcomputadoras.
ISLISPISLISP es una especificación Lisp desarrollada en la década de 1990 y publicada por ISO en 1997 [8] . La especificación se actualizó en 2007 [9] . ISLISP es un intento de estandarizar el núcleo de Lisp mediante la consolidación de dialectos Lisp comerciales existentes y en desarrollo en el momento de su creación. El dialecto es en muchos aspectos similar a Common Lisp (ámbito léxico, espacios de nombres separados para funciones y variables, sistema de tipos de datos bastante potente, soporte para tipos complejos, sistema de macros, sistema de objetos), pero de menor alcance. Para 2018, hay alrededor de una docena de implementaciones importantes de ISLISP, lanzadas principalmente bajo licencias propietarias.
openlispUn dialecto creado por Christian Julien en 1988. Originalmente llamado MLisp, pasó a llamarse OpenLisp en 1993. El nombre simboliza el uso de estándares abiertos, pero no tiene nada que ver con la Open Source Initiative o el software libre : el sistema se distribuye bajo una licencia propietaria.
Cumple totalmente con la especificación ISLISP , además implementa una serie de características que faltan en este estándar. El desarrollo interactivo ( REPL ) es posible en el entorno Emacs . Además del intérprete, el sistema contiene un compilador que convierte el código fuente en LAP (Lisp Assembly Program, código ensamblador de bajo nivel en formato de listas Lisp) y un generador de código que compila el programa LAP en código fuente. en el lenguaje C. Se presta mucha atención a la interacción con código C/C++ y Java, compatibilidad con la integración en sistemas de software como intérprete de lenguaje integrado. El sistema continúa siendo desarrollado y soportado, hay versiones para la mayoría de los sistemas operativos y plataformas de hardware disponibles.
Pico LispPicoLisp es una implementación gratuita de Lisp diseñada para su uso en Linux y otros sistemas POSIX . El proyecto apareció a fines de la década de 1980, su objetivo era crear un sistema Lisp minimalista pero práctico para computadoras personales.
Desde el punto de vista del idioma, PicoLisp distingue entre mayúsculas y minúsculas, es compatible con UTF-8 y es extremadamente simple. El kernel solo admite tres tipos de datos: números, cadenas y listas. Se han introducido medios sintácticos para controlar la computabilidad de los parámetros, la forma de cotización se ha extendido a un número indefinido de parámetros. Esta solución eliminó la necesidad de una sintaxis especial para macros y una expresión lambda. Las estructuras y las matrices no son compatibles, hay un subsistema de objetos potente pero económicamente ejecutado. A diferencia de Common Lisp, el lenguaje utiliza enlaces de parámetros dinámicos .
El entorno PicoLisp no tiene un compilador (esta es una decisión fundamental, por lo que se hicieron muchas simplificaciones en el lenguaje), pero incluye un servidor de aplicaciones y un subsistema de almacenamiento integrados [10] . Implementé una llamada directa a funciones externas escritas en C, así como interacción con código Java. El dialecto continúa desarrollándose, se lanzan regularmente nuevas versiones del entorno de programación para varias plataformas.
EuLisp
Un dialecto desarrollado desde 1985 por una comunidad de desarrolladores y usuarios europeos de Lisp de la academia y la industria. Puede verse como una especie de "respuesta europea a Common Lisp", un intento alternativo de crear un único dialecto de Lisp adecuado para una amplia gama de tareas. El objetivo del proyecto era crear un lenguaje "Lisp pasado" eficiente, compacto y sin trabas. Una de las prioridades en el desarrollo de este proyecto fue agregar funciones orientadas a objetos a Lisp.
El dialecto admite la definición de módulos con alcance léxico, cierres con enlace dinámico, un espacio de nombres único para variables y funciones, como en Scheme, soporte incorporado para concurrencia, un sistema de objetos con herencia única y múltiple. Una característica del proyecto es la división del lenguaje en dos niveles, Nivel-0 y Nivel-1, algunas características están disponibles solo en el nivel 1, por ejemplo, herencia múltiple y metaobjetos.
La última especificación oficial (versión .99) se lanzó en 1993, su revisión no oficial (versión .991) en 2010. El primer intérprete se lanzó en 1990, en la década de 1990-2000 se crearon varias implementaciones, cuyo desarrollo cesó en 2011.
nuevoLispUn dialecto desarrollado en 1991 por Lutz Müller para su uso como lenguaje de secuencias de comandos en estaciones de trabajo Sun que ejecutan SunOS y FreeBSD. Actualmente disponible en plataformas Intel de 32 y 64 bits bajo FreeBSD, Linux, Windows, Mac. Difiere en simplicidad, volumen pequeño, alcance dinámico de variables, tiene una serie de características en la sintaxis. Admite el sistema de objetos FOOP, las herramientas de comunicación entre procesos y más. El entorno incluye un intérprete, un mini-IDE con un editor de código y un emulador de terminal para desarrollo interactivo, bibliotecas para desarrollo web y redes a través de protocolos TCP y UDP.
RaquetaDesarrollado desde 1994 por PLT Inc. un dialecto originalmente llamado PLT Scheme. Es un descendiente del dialecto Scheme, pero lo expande significativamente. Uno de los objetivos de la creación de Racket era proporcionar una plataforma para el desarrollo y la implementación de lenguajes específicos de dominio. Su rasgo característico es un macrosistema muy potente que le permite crear nuevas construcciones sintácticas e incluso lenguajes. Al mismo tiempo, el sistema de módulos asegura el uso en un solo programa de elementos escritos en diferentes dialectos con diferente semántica.
La implementación incluye un compilador, un sistema de tiempo de ejecución, un compilador JIT, un entorno de desarrollo integrado con un conjunto de herramientas, bibliotecas. El IDE de DrRacket, escrito en el propio Racket, contiene un perfilador, un depurador y un sistema de prueba unitaria. Hay bibliotecas disponibles que admiten programación de sistemas y redes, desarrollo web, una interfaz única para el sistema operativo, una interfaz para llamar a funciones externas, varias variantes de expresiones regulares, analizadores y generadores de analizadores, herramientas de programación lógica y una interfaz gráfica de usuario avanzada. La plataforma está implementada para Windows, MacOS, Linux y otras variantes de UNIX. Se distribuye como software gratuito y de código abierto bajo la Licencia Pública General Menor de GNU (LGPL). Se mantiene un repositorio centralizado para paquetes y extensiones creados por la comunidad.
Se utiliza como lenguaje de investigación (principalmente como plataforma para el desarrollo de lenguajes y herramientas de programación), educativo, de scripting , industrial (en particular, para el desarrollo de videojuegos). En el tutorial , el tutorial usa Bootstrap en el concepto de "aprender codificando juegos".
ArcoArc es un dialecto creado por Paul Graham . El objetivo del proyecto declarado por el autor es crear un sistema Lisp simple, construido sobre un conjunto mínimo de entidades definidas económicamente, con un conjunto de bibliotecas útiles en la práctica, enfocado al uso profesional y que permita "Lisp hacks" efectivos (varios trucos computacionales basado en las características de Lisp). El autor básicamente se negó a admitir algunas tecnologías en Arc, en particular, OOP , ya que consideró que solo se necesitan cuando se desarrollan dentro de grandes organizaciones, y por sí mismas no dan un efecto útil real. El desarrollo se anunció en 2001, la primera versión pública apareció en 2008. La primera implementación del lenguaje se escribió en el entorno Racket. Desde 2009, el sistema original prácticamente ha dejado de desarrollarse, y ahora el desarrollo de Arc continúa en varias bifurcaciones .
En las últimas décadas se han generalizado los lenguajes que utilizan la gestión automática de la memoria, la compilación en código intermedio y su ejecución en una máquina virtual, como Java, Python, C# y otros. También se han creado varios dialectos de Lisp, orientados a la ejecución en los entornos dinámicos de otros lenguajes. Estos dialectos pueden trabajar directamente con las bibliotecas del entorno de idioma correspondiente e interactuar con programas en otros idiomas que se ejecutan en el mismo entorno. Entre ellos:
Básicamente, los fundamentos ideológicos del estándar fueron influenciados por MACLisp y sus dialectos, se tomaron prestadas una gran cantidad de características de InterLISP y nuevos sistemas como Zetalisp y NIL.
Common Lisp es un lenguaje con vinculación de variables estáticas, representación tradicional de funciones (las funciones no son "ciudadanos completos"), admite macros, funcionales, cierres léxicos. Es decir, desde el punto de vista de la parte funcional del lenguaje, contiene todo el conjunto de medios sintácticos que se ha desarrollado en Lisp durante el último cuarto de siglo y es suficiente para cualquier aplicación de programación funcional y extensión del lenguaje en cualquier dirección deseada. Las funciones del sistema en Common Lisp conservan sus nombres tradicionales, pero muchas de ellas tienen sinónimos con nombres más descriptivos, por ejemplo, las funciones CAR(obtener el encabezado de una lista) y CDR(obtener el final de una lista) tienen sinónimos para FIRST("primero" ) y REST("resto"), respectivamente. .
Dado que el objetivo era desarrollar un sistema adecuado para la gama más amplia posible de aplicaciones, la especificación se amplía significativamente con funciones, medios sintácticos y mecanismos que no son característicos del Lisp original. Así, por ejemplo, se han añadido al lenguaje casi todas las construcciones sintácticas existentes en los lenguajes imperativos, incluidos varios tipos de bucles. El sistema de objetos CLOS (Common Lisp Object System) no se incluyó originalmente en el estándar, pero se convirtió en parte de él más tarde. Common Lisp es adecuado para escribir programas tanto en estilo funcional como directivo, es posible generalizar la programación (usando macros estándar), programación de producción, existen herramientas para organizar la lógica, la programación de objetos y la programación basada en datos. La especificación no incluye una descripción detallada del entorno de programación, definiendo solo en los términos más generales su composición y principios de interacción de elementos.
Los críticos del nuevo estándar señalaron su exageración y énfasis excesivo en los requisitos prácticos, lo que condujo a una violación de la "pureza funcional" de Lisp y un aumento en el tamaño del sistema Lisp. Sin embargo, bajo la presión del Departamento de Defensa de los EE. UU. y en parte con su apoyo financiero, se crearon implementaciones de Common Lisp en la segunda mitad de la década de 1980 para casi todas las plataformas comunes.
Una importante revisión de la norma, publicada en 1984, tuvo lugar en 1990 :
En 1995, Common Lisp fue estandarizado por ANSI . El estándar prácticamente repetía la especificación de 1990, los cambios son menores y consisten principalmente en la adición, eliminación y cambio de nombre de operadores y variables del sistema y cambios en las llamadas al sistema. Podemos notar la aparición en Common Lisp del tipo booleano (boolean), cuyos valores solo pueden ser NIL y T.
Un programa de ejemplo que muestra el mensaje " ¡Hola, mundo!" »:
( formato t "¡Hola, mundo!~%" )Variantes de Quine (un programa que genera su código fuente) en Lisp:
(( lambda ( x ) ( lista x ( lista 'cita x ))) ( cita ( lambda ( x ) ( lista x ( lista 'cita x ))))) (( lambda ( x ) ( lista x ( lista 'cita x ))) ' ( lambda ( x ) ( lista x ( lista 'cita x ))))Ambos funcionarán en la mayoría de los dialectos de Lisp, incluido Scheme . Cuál de ellos será más preciso depende de la implementación del sistema Lisp: en algunos, al mostrar un valor de lista para mostrar el bloqueo del cálculo, el operador especial quotese muestra como un nombre completo (la primera opción es adecuada para ellos) , en otros, como un apóstrofo (la segunda opción). Versión Common Lisp de Quine usando backquote:
(( lambda ( x ) ` ( , x ',x )) ' ( lambda ( x ) ` ( , x ',x )))Una versión iterativa de la función para determinar el N-ésimo número de Fibonacci utilizando la macro Loop:
( defun fibonacci ( n ) ( loop repetir n para a = 0 luego b y b = 1 luego ( + a b ) finalmente ( return b )))Versión recursiva de la función del número N de Fibonacci:
( defun fibonacci ( n ) ( if ( > n 1 ) ( + ( fibonacci ( - n 1 )) ( fibonacci ( - n 2 ))) n ))Función recursiva para calcular una potencia entera arbitraria (algoritmo con tiempo de ejecución logarítmico y profundidad de recursión):
( defun power ( x n ) ( cond (( minusp n ) ( / 1 ( power x ( - n )))) (( zerop n ) 1 ) (( evenp n )( power ( * x x ) ( / n 2 ))) ( t ( * x ( potencia ( * x x ) ( / ( - n 1 ) 2 ))))))Aquí se utilizan los predicados del sistema ZEROP - comprobar la igualdad a cero, MINUSP - comprobar la negatividad, EVENP - comprobar la paridad.
1955 | 1960 | 1965 | 1970 | 1975 | 1980 | 1985 | 1990 | 1995 | 2000 | 2005 | 2010 | 2015 | 2018 | ||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Ceceo 1.5 | Ceceo 1.5 | ||||||||||||||||||||
maclisp | maclisp | ||||||||||||||||||||
entrececeo | entrececeo | ||||||||||||||||||||
ZetaLisp | ceceo máquina ceceo | ||||||||||||||||||||
Esquema | Esquema | ||||||||||||||||||||
NULO | NULO | ||||||||||||||||||||
ceceo común | ceceo común | ||||||||||||||||||||
LeLisp | Le_Lisp | ||||||||||||||||||||
T | T | ||||||||||||||||||||
Lisp de Emacs | Lisp de Emacs | ||||||||||||||||||||
AutoLISP | AutoLISP | ||||||||||||||||||||
lisp abierto | lisp abierto | ||||||||||||||||||||
pico ceceo | pico ceceo | ||||||||||||||||||||
EuLisp | EuLisp | ||||||||||||||||||||
ISLISP | ISLISP | ||||||||||||||||||||
nuevoLISP | nuevoLISP | ||||||||||||||||||||
raqueta | raqueta | ||||||||||||||||||||
Engaño | Astucia de GNU | ||||||||||||||||||||
Visual LISP | Visual LISP | ||||||||||||||||||||
clausura | clausura | ||||||||||||||||||||
Arco | Arco | ||||||||||||||||||||
LFE | LFE | ||||||||||||||||||||
hola | hola |
El alcance del lenguaje Lisp es diverso: ciencia e industria, educación y medicina, desde la decodificación del genoma humano hasta el sistema de diseño de aeronaves. Las primeras áreas de aplicación del lenguaje Lisp estaban asociadas con el procesamiento de datos simbólicos y los procesos de toma de decisiones. El dialecto más popular de Common Lisp hoy en día es un lenguaje de programación universal. Es ampliamente utilizado en una variedad de proyectos: servidores y servicios de Internet, servidores de aplicaciones y clientes que interactúan con bases de datos relacionales y de objetos, cálculos científicos y programas de juegos.
Hay dialectos especializados de Lisp diseñados para aplicaciones específicas, por ejemplo, Game Oriented Assembly Lisp (GOAL) fue creado para escribir juegos tridimensionales altamente dinámicos, toda la serie de juegos Jak and Daxter está escrita en él .
Uno de los usos de Lisp es su uso como lenguaje de secuencias de comandos que automatiza el trabajo en varios programas de aplicación, que incluyen:
En el caso de Lisp, es difícil trazar una línea clara entre un dialecto y un idioma descendiente, ya que varios dialectos de Lisp, creados durante más de medio siglo de su existencia, pueden diferir significativamente y ser incompatibles. Por otro lado, Lisp, simplemente en virtud de su edad, ha tenido una u otra influencia en una gran cantidad de lenguajes, y no solo en los funcionales. Si consideramos a los descendientes directos de Lisp solo lenguajes que han conservado la estructura general del programa, pero son sintácticamente incompatibles con Lisp, entonces podemos distinguir:
A principios de la década de 1970, se reconocieron las limitaciones impuestas por el sistema de tiempo compartido a los usuarios de software interactivo (que incluye tanto los sistemas Lisp como la mayoría de los programas escritos en Lisp). Además, Lisp es relativamente costoso para el soporte dinámico, incluida la verificación de tipos en tiempo de ejecución y la recolección periódica de basura. En 1973 surge la idea de desarrollar una computadora personal (workstation), diseñada, a partir del hardware, específicamente para lograr la más eficiente ejecución de programas Lisp, incluyendo soporte hardware para cálculos lambda y tipado dinámico.
En los EE. UU., el desarrollo de la computadora Lisp tuvo lugar en la década de 1970 en el Centro de Investigación de Palo Alto de Xerox Corporation y en el MIT (este último patrocinado por DARPA ). Su resultado fue la aparición a principios y mediados de la década de 1980 de tres grandes fabricantes: Xerox, Lisp Machine Inc. (LMI) y Symbolics Inc. Xerox fabricó máquinas Lisp compatibles con Interlisp, las dos últimas empresas procedían del MIT y se centraron en Zetalisp. Un poco más tarde, Texas Instruments se hizo cargo de la producción de máquinas Lisp . En Japón, en 1984, se mostró el primer prototipo de la máquina comercial Alpha lisp de Fujitsu .
Las máquinas Lisp tenían una arquitectura de hardware enfocada en el procesamiento de listas y la programación funcional, con soporte de hardware para recolección de basura, escritura dinámica. Tenían entornos de desarrollo integrados que contenían miles de funciones e incluían todos los componentes que actualmente forman los IDE de lenguaje de alto nivel . Interfaz gráfica de usuario compatible con múltiples ventanas , trabajo con el mouse y otras herramientas de posicionamiento adicionales ( trackball , lápiz óptico ), gráficos y sonido de E/S de alta calidad. Aunque estaban orientados a Lisp, otros lenguajes de alto nivel también estaban disponibles en las máquinas Lisp y proporcionaban los medios para la interoperabilidad entre idiomas. Lisp proporcionó trabajo en modo interpretado y compilación de programas en código objeto.
Para su época, las máquinas Lisp se encontraban entre las computadoras más poderosas en la clase de estaciones de trabajo personales. Se predijo que tendrían un gran futuro, pero en la década de 1990 todos cayeron en desuso y los fabricantes cesaron sus operaciones o se reorientaron hacia la producción de computadoras de propósito general. La razón fue que en el contexto de un largo crecimiento exponencial en la velocidad y la memoria de las computadoras , el desarrollo de equipos "bajo el lenguaje" resultó inútil: las computadoras de propósito general en rápido desarrollo equipadas con traductores Lisp superaron a las máquinas Lisp en su capacidades que, por su especialización, eran más caras y perdían versatilidad.
En la URSS, el trabajo relacionado con el uso de Lisp y la creación de sus propios sistemas Lisp se hizo más activo a partir de 1968, cuando un grupo de científicos estadounidenses, entre los que se encontraban McCarthy y B. Berkeley[ aclarar ] visitó la Unión Soviética. En Novosibirsk, en el Centro de Computación de la Rama Siberiana de la Academia de Ciencias, donde McCarthy pasó la mayor parte de su tiempo, sentó las bases para la implementación de Lisp en BESM-6 . En Moscú, en el Centro de Computación de la Academia de Ciencias de la URSS, los matemáticos soviéticos Lavrov y Silagadze , con la ayuda de Berkeley, comenzaron a trabajar en su propia versión del intérprete Lisp para BESM-6. Posteriormente, Lavrov se fue a trabajar a la Universidad Estatal de Leningrado y Silagadze, al Centro de Computación de la Academia de Ciencias de Georgia en Tbilisi, donde continuaron trabajando con Lisp y participaron en la creación de varios sistemas Lisp para computadoras ES . [once]
En Leningrado se creó un sistema Lisp para la computadora polaca Odra 1204, en Moscú se creó una implementación para BESM-6 compatible con la versión en inglés de Lisp para la computadora ICL 4, aparecieron implementaciones para la computadora ES en MPEI y Far Centro Científico del Este en Vladivostok. En el Instituto para Problemas de Transmisión de Información (Moscú) a fines de la década de 1970, se creó el sistema lisp EKLISP para la minicomputadora ECLIPS. Las computadoras de fabricación occidental en la URSS usaban Stanford Lisp y UT-Lisp (Dubna, IBM 370 y CDC 6600). También fue popular el sistema sueco Nordström (Lisp en Fortran).
En 1975 tuvo lugar en Tbilisi la cuarta conferencia internacional sobre los problemas de la inteligencia artificial IJCAI-75, lo que contribuyó a aumentar el interés por Lisp y difundirlo entre universidades e institutos de investigación. En 1978, Svyatoslav Lavrov y Givi Silagadze publicaron el primer libro de texto de Lisp en ruso ("Procesamiento automático de datos. El lenguaje LISP y su implementación").
En la década de 1980, el interés por Lisp en la URSS continuó, sin embargo, se publicó muy poca literatura sobre el lenguaje (se publicaron dos libros en una década, ambos traducidos: "Programación funcional. Aplicación e implementación" de Henderson, traducido en 1983, y un "The World of Lisp" en dos volúmenes de Hyvönen y Seppänen, cuya traducción se publicó en 1990).
En la Rusia postsoviética, el uso de Lisp se limita principalmente a la investigación académica y al trabajo de entusiastas individuales. Además, Lisp se sigue utilizando con fines educativos en algunas universidades rusas, pero incluso aquí en los últimos años se ha desplazado notablemente: como lenguaje de propósito general, no se enseña ni se utiliza, y los estudiantes más jóvenes a menudo prefieren utilizarlo como lenguajes educativos para la enseñanza de la programación funcional, lenguajes funcionales que han surgido en las últimas dos décadas. Sin embargo, el interés por el lenguaje permanece, como lo demuestra la aparición de trabajos impresos originales y traducidos en Lisp, que se reanudó en los años 2010-2020 [12] .
Ceceo | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Características |
| ||||||||||||||
Implementaciones |
| ||||||||||||||
Hardware |
| ||||||||||||||
Comunidad |
| ||||||||||||||
|
Lenguajes de programación | |
---|---|
|