stringtranslate.com

ceceo común

Common Lisp ( CL ) es un dialecto del lenguaje de programación Lisp , publicado en el documento estándar ANSI INCITS 226-1994 (S2018) [1] del American National Standards Institute (ANSI) (anteriormente X3.226-1994 (R1999) ). [2] Common Lisp HyperSpec , una versión HTML con hipervínculo , se deriva del estándar ANSI Common Lisp. [3]

El lenguaje Common Lisp fue desarrollado como un sucesor estandarizado y mejorado de Maclisp . A principios de la década de 1980, varios grupos ya estaban trabajando en diversos sucesores de MacLisp: Lisp Machine Lisp (también conocido como ZetaLisp), Spice Lisp , NIL y S-1 Lisp . Common Lisp buscó unificar, estandarizar y ampliar las características de estos dialectos MacLisp. Common Lisp no es una implementación, sino más bien una especificación de lenguaje . [4] Se encuentran disponibles varias implementaciones del estándar Common Lisp, incluido software gratuito y de código abierto y productos propietarios. [5] Common Lisp es un lenguaje de programación multiparadigma de propósito general . Admite una combinación de paradigmas de programación procedimental , funcional y orientada a objetos . Como lenguaje de programación dinámico , facilita el desarrollo de software evolutivo e incremental , con compilación iterativa en programas de tiempo de ejecución eficientes. Este desarrollo incremental a menudo se realiza de forma interactiva sin interrumpir la aplicación en ejecución.

También admite anotación y conversión de tipos opcionales, que se pueden agregar según sea necesario en las etapas posteriores de creación de perfiles y optimización, para permitir que el compilador genere código más eficiente. Por ejemplo, fixnumpuede contener un número entero sin caja en un rango admitido por el hardware y la implementación, lo que permite una aritmética más eficiente que los números enteros grandes o los tipos de precisión arbitrarios. De manera similar, se puede indicar al compilador por módulo o por función qué tipo de nivel de seguridad se desea, utilizando declaraciones de optimización .

Common Lisp incluye CLOS , un sistema de objetos que admite múltiples métodos y combinaciones de métodos. A menudo se implementa con un protocolo Metaobject .

Common Lisp es extensible a través de funciones estándar como macros Lisp (transformaciones de código) y macros de lectura (analizadores de entrada de caracteres).

Common Lisp proporciona compatibilidad parcial con Maclisp y el Lisp original de John McCarthy . Esto permite que el software Lisp más antiguo se transfiera a Common Lisp. [6]

Historia

El trabajo en Common Lisp comenzó en 1981 después de una iniciativa del gerente de ARPA, Bob Engelmore, para desarrollar un dialecto Lisp estándar comunitario único. [7] Gran parte del diseño inicial del lenguaje se realizó mediante correo electrónico. [8] [9] En 1982, Guy L. Steele Jr. dio la primera descripción general de Common Lisp en el Simposio ACM de 1982 sobre LISP y programación funcional. [10]

La primera documentación en idioma se publicó en 1984 como Common Lisp the Language (conocido como CLtL1), primera edición. Una segunda edición (conocida como CLtL2), publicada en 1990, incorporó muchos cambios al lenguaje, realizados durante el proceso de estandarización de ANSI Common Lisp: sintaxis LOOP extendida, el sistema de objetos Common Lisp, el sistema de condiciones para el manejo de errores, una interfaz para el Bonita impresora y mucho más. Pero CLtL2 no describe el estándar ANSI Common Lisp final y, por lo tanto, no es una documentación de ANSI Common Lisp. El estándar ANSI Common Lisp final se publicó en 1994. Desde entonces no se ha publicado ninguna actualización del estándar. Las implementaciones y bibliotecas han proporcionado varias extensiones y mejoras a Common Lisp (ejemplos son Unicode, Concurrency, IO basado en CLOS) .

Sintaxis

Common Lisp es un dialecto de Lisp. Utiliza expresiones S para indicar tanto el código como la estructura de datos. Las llamadas a funciones, formularios de macros y formularios especiales se escriben como listas, con el nombre del operador primero, como en estos ejemplos:

 ( + 2 2 ) ; suma 2 y 2, lo que da 4. El nombre de la función es '+'. Lisp no tiene operadores como tales.   
 ( defvar *x* ) ; Garantiza que exista una variable *x*, ; sin darle un valor. Los asteriscos son parte de ; el nombre, que por convención denota una variable especial (global). ; Al símbolo *x* también se le otorga la propiedad de que ; las vinculaciones posteriores son dinámicas, más que léxicas. ( setf *x* 42.1 ) ; Establece la variable *x* al valor de punto flotante 42.1          
 ;; Defina una función que eleve un número al cuadrado: ( defun cuadrado ( x ) ( * x x ))      
 ;; Ejecutar la función: ( cuadrado 3 ) ; Devuelve 9   
 ;; La construcción 'let' crea un ámbito para variables locales. Aquí ;; la variable 'a' está ligada a 6 y la variable 'b' está ligada ;; a 4. Dentro de 'let' hay un 'cuerpo', donde se devuelve el último valor calculado. ;; Aquí el resultado de sumar a y b se devuelve a partir de la expresión 'let'. ;; Las variables a y b tienen alcance léxico, a menos que los símbolos hayan sido ;; marcadas como variables especiales (por ejemplo, mediante un DEFVAR anterior). ( sea (( a 6 ) ( b 4 )) ( + a b )) ; regresa 10              

Tipos de datos

Common Lisp tiene muchos tipos de datos .

tipos escalares

Los tipos de números incluyen números enteros , proporciones , números de punto flotante y números complejos . [11] Common Lisp utiliza bignums para representar valores numéricos de tamaño y precisión arbitrarios. El tipo de relación representa fracciones exactamente, una función que no está disponible en muchos idiomas. Common Lisp fuerza automáticamente los valores numéricos entre estos tipos según corresponda.

El tipo de carácter Common Lisp no se limita a caracteres ASCII . La mayoría de las implementaciones modernas permiten caracteres Unicode . [12]

El tipo de símbolo es común a los lenguajes Lisp, pero en gran medida desconocido fuera de ellos. Un símbolo es un objeto de datos con nombre único que consta de varias partes: nombre, valor, función, lista de propiedades y paquete. De ellas, la celda de valor y la celda de función son las más importantes. Los símbolos en Lisp se usan a menudo de manera similar a los identificadores en otros lenguajes: para contener el valor de una variable; sin embargo hay muchos otros usos. Normalmente, cuando se evalúa un símbolo, se devuelve su valor. Algunos símbolos se evalúan a sí mismos; por ejemplo, todos los símbolos del paquete de palabras clave se autoevalúan. Los valores booleanos en Common Lisp están representados por los símbolos de autoevaluación T y NIL. Common Lisp tiene espacios de nombres para símbolos, llamados "paquetes".

Hay varias funciones disponibles para redondear valores numéricos escalares de varias maneras. La función roundredondea el argumento al número entero más cercano, y en los casos intermedios se redondea al número entero par. Las funciones truncate, floory ceilingredondean hacia cero, hacia abajo o hacia arriba respectivamente. Todas estas funciones devuelven la parte fraccionaria descartada como valor secundario. Por ejemplo, (floor -2.5)produce −3, 0,5; (ceiling -2.5)produce −2, −0,5; (round 2.5)produce 2, 0,5; y (round 3.5)produce 4, −0,5.

Estructuras de datos

Los tipos de secuencia en Common Lisp incluyen listas, vectores, vectores de bits y cadenas. Hay muchas operaciones que pueden funcionar en cualquier tipo de secuencia.

Como en casi todos los demás dialectos de Lisp, las listas en Common Lisp se componen de cons , a veces llamadas células o pares de cons . Una desventaja es una estructura de datos con dos ranuras, llamadas car y cdr . Una lista es una cadena enlazada de contras o la lista vacía. El coche de cada contra se refiere a un miembro de la lista (posiblemente otra lista). El cdr de cada contra se refiere a los siguientes contras, excepto los últimos contras de una lista, cuyo cdr se refiere al nilvalor. Los conses también se pueden utilizar fácilmente para implementar árboles y otras estructuras de datos complejas; aunque normalmente se recomienda utilizar instancias de estructura o clase en su lugar. También es posible crear estructuras de datos circulares con conses.

Common Lisp admite matrices multidimensionales y puede cambiar dinámicamente el tamaño de matrices ajustables si es necesario. Las matrices multidimensionales se pueden utilizar para matemáticas matriciales. Un vector es una matriz unidimensional. Los arreglos pueden llevar cualquier tipo como miembros (incluso tipos mixtos en el mismo arreglo) o pueden especializarse para contener un tipo específico de miembros, como en un vector de bits. Normalmente, sólo se admiten unos pocos tipos. Muchas implementaciones pueden optimizar las funciones de la matriz cuando la matriz utilizada está especializada en tipos. Dos tipos de matrices especializadas en tipos son estándar: una cadena es un vector de caracteres, mientras que un vector de bits es un vector de bits .

Las tablas hash almacenan asociaciones entre objetos de datos. Cualquier objeto puede usarse como clave o valor. Las tablas hash cambian de tamaño automáticamente según sea necesario.

Los paquetes son colecciones de símbolos que se utilizan principalmente para separar las partes de un programa en espacios de nombres . Un paquete puede exportar algunos símbolos, marcándolos como parte de una interfaz pública. Los paquetes pueden utilizar otros paquetes.

Las estructuras , de uso similar a las estructuras C y los registros Pascal , representan estructuras de datos complejas arbitrarias con cualquier número y tipo de campos (llamados espacios ). Las estructuras permiten una herencia única.

Las clases son similares a las estructuras, pero ofrecen características más dinámicas y herencia múltiple. (Ver CIERRE ). Las clases se agregaron tarde a Common Lisp y existe cierta superposición conceptual con las estructuras. Los objetos creados a partir de clases se denominan Instancias . Un caso especial son las funciones genéricas. Las funciones genéricas son funciones e instancias.

Funciones

Common Lisp admite funciones de primera clase . Por ejemplo, es posible escribir funciones que tomen otras funciones como argumentos o que también devuelvan funciones. Esto permite describir operaciones muy generales.

La biblioteca Common Lisp depende en gran medida de funciones de orden superior. Por ejemplo, la sortfunción toma un operador relacional como argumento y una función clave como argumento de palabra clave opcional. Esto se puede utilizar no sólo para ordenar cualquier tipo de datos, sino también para ordenar estructuras de datos según una clave.

 ;; Ordena la lista utilizando la función > y < como operador relacional. ( ordenar ( lista 5 2 6 3 1 4 ) #' > ) ; Devuelve (6 5 4 3 2 1) ( ordenar ( lista 5 2 6 3 1 4 ) #' < ) ; Devoluciones (1 2 3 4 5 6)                    
 ;; Ordena la lista según el primer elemento de cada sublista. ( ordenar ( lista ' ( 9 A ) ' ( 3 B ) ' ( 4 C )) #' < :clave #' primero ) ; Devuelve ((3 B) (4 C) (9 A))            

El modelo de evaluación de funciones es muy sencillo. Cuando el evaluador encuentra un formulario (f a1 a2...), supone que el símbolo denominado f es uno de los siguientes:

  1. Un operador especial (fácil de comparar con una lista fija)
  2. Un macro operador (debe haber sido definido previamente)
  3. El nombre de una función (predeterminada), que puede ser un símbolo o un subformulario que comienza con el símbolo lambda.

Si f es el nombre de una función, entonces los argumentos a1, a2, ..., an se evalúan en orden de izquierda a derecha y la función se encuentra e invoca con esos valores proporcionados como parámetros.

Definiendo funciones

La macrodefun define funciones donde una definición de función proporciona el nombre de la función, los nombres de los argumentos y el cuerpo de la función:

 ( cuadrado defun ( x ) ( * x x ))     

Las definiciones de funciones pueden incluir directivas del compilador , conocidas como declaraciones , que brindan sugerencias al compilador sobre la configuración de optimización o los tipos de datos de los argumentos. También pueden incluir cadenas de documentación (docstrings), que el sistema Lisp puede utilizar para proporcionar documentación interactiva:

 ( defun cuadrado ( x ) "Calcula el cuadrado del flotador único x." ( declarar ( flotador único x ) ( optimizar ( velocidad 3 ) ( depurar 0 ) ( seguridad 1 ))) ( el flotador único ( * x X )))                  

Las funciones anónimas ( literales de función ) se definen mediante lambdaexpresiones, por ejemplo, (lambda (x) (* x x))para una función que eleva al cuadrado su argumento. El estilo de programación Lisp utiliza con frecuencia funciones de orden superior para las cuales es útil proporcionar funciones anónimas como argumentos.

Las funciones locales se pueden definir con flety labels.

 ( flet (( cuadrado ( x ) ( * x x ))) ( cuadrado 3 ))       

Hay varios otros operadores relacionados con la definición y manipulación de funciones. Por ejemplo, se puede compilar una función con el compileoperador. (Algunos sistemas Lisp ejecutan funciones utilizando un intérprete de forma predeterminada a menos que se les indique que lo compilen; otros compilan todas las funciones).

Definición de funciones y métodos genéricos.

La macro defgenericdefine funciones genéricas . Las funciones genéricas son una colección de métodos . La macro defmethoddefine métodos.

Los métodos pueden especializar sus parámetros en clases estándar CLOS , clases de sistema , clases de estructura u objetos individuales. Para muchos tipos, existen clases de sistema correspondientes .

Cuando se llama a una función genérica, el envío múltiple determinará el método efectivo a utilizar.

 ( añadir defgenérico ( a b ))   
 ( método de definición agregar (( un número ) ( b número )) ( + a b ))        
 ( método de definición agregar (( un vector ) ( b número )) ( map 'vector ( lambda ( n ) ( + n b )) a ))             
 ( método de definición agregar (( un vector ) ( b vector )) ( mapa 'vector #' + a b ))          
( defmethod add (( una cadena ) ( b cadena )) ( concatenar 'cadena a b ))         
 ( sumar 2 3 ) ; devuelve 5 ( suma #( 1 2 3 4 ) 7 ) ; devuelve #(8 9 10 11) ( agregar #( 1 2 3 4 ) #( 4 3 2 1 )) ; devuelve #(5 5 5 5) ( agregar "COMÚN" "LISP" ) ; devuelve "LISP COMÚN"                        

Las funciones genéricas también son un tipo de datos de primera clase . Hay muchas más funciones para las funciones y métodos genéricos que las descritas anteriormente.

El espacio de nombres de la función

El espacio de nombres para los nombres de funciones está separado del espacio de nombres para las variables de datos. Ésta es una diferencia clave entre Common Lisp y Scheme . Para Common Lisp, los operadores que definen nombres en el espacio de nombres de la función incluyen defun,, y .fletlabelsdefmethoddefgeneric

Para pasar una función por su nombre como argumento a otra función, se debe utilizar el functionoperador especial, comúnmente abreviado como #'. El primer sortejemplo anterior se refiere a la función nombrada por el símbolo >en el espacio de nombres de la función, con el código #'>. Por el contrario, para llamar a una función pasada de esa manera, se usaría el funcalloperador en el argumento.

El modelo de evaluación de Scheme es más simple: solo hay un espacio de nombres y se evalúan todas las posiciones en el formulario (en cualquier orden), no solo los argumentos. Por lo tanto, el código escrito en un dialecto a veces resulta confuso para los programadores con más experiencia en el otro. Por ejemplo, a muchos programadores de Common Lisp les gusta usar nombres de variables descriptivos como lista o cadena , lo que podría causar problemas en Scheme, ya que ocultarían localmente los nombres de funciones.

Si un espacio de nombres separado para funciones es una ventaja es un motivo de controversia en la comunidad Lisp. Generalmente se lo conoce como el debate Lisp-1 vs. Lisp-2 . Lisp-1 se refiere al modelo de Scheme y Lisp-2 se refiere al modelo de Common Lisp. Estos nombres fueron acuñados en un artículo de 1988 de Richard P. Gabriel y Kent Pitman , que compara ampliamente los dos enfoques. [13]

Múltiples valores de retorno

Common Lisp admite el concepto de valores múltiples , [14] donde cualquier expresión siempre tiene un único valor primario , pero también puede tener cualquier número de valores secundarios , que pueden ser recibidos e inspeccionados por personas interesadas. Este concepto es distinto de devolver un valor de lista, ya que los valores secundarios son completamente opcionales y se pasan a través de un canal lateral dedicado. Esto significa que las personas que llaman pueden permanecer completamente inconscientes de que los valores secundarios están ahí si no los necesitan, y hace que sea conveniente utilizar el mecanismo para comunicar información que a veces es útil, pero no siempre necesaria. Por ejemplo,

( let (( x 1266778 ) ( y 458 )) ( enlace de valor múltiple ( resto del cociente ) ( truncar x y ) ( formato nil "~A dividido por ~A es ~A resto ~A" resto del cociente x y )) )                 ;;;; => "1266778 dividido por 458 es 2765 resto 408"
( defun get-answer ( biblioteca ) ( gethash ' biblioteca de respuestas 42 ))      ( defun the-answer-1 ( biblioteca ) ( formato nil "La respuesta es ~A" ( obtener-respuesta biblioteca ))) ;;;; Devuelve "La respuesta es 42" si la RESPUESTA no está presente en la BIBLIOTECA       ( defun the-answer-2 ( biblioteca ) ( enlace de valores múltiples ( respuesta segura-p ) ( biblioteca get-answer ) ( si ( no estoy seguro-p ) "No lo sé" ( formato nulo "La respuesta es ~Una" respuesta )))) ;;;; Devuelve "No sé" si la RESPUESTA no está presente en la BIBLIOTECA               

Los valores múltiples son compatibles con un puñado de formularios estándar, los más comunes de los cuales son el MULTIPLE-VALUE-BINDformulario especial para acceder a valores secundarios y VALUESpara devolver valores múltiples:

( defun magic-eight-ball () "Devuelve una predicción de perspectiva, con la probabilidad como valor secundario" ( valores "Perspectiva buena" ( aleatorio 1.0 )))       ;;;; => "Buenas perspectivas" ;;;; => 0,3187

Otros tipos

Otros tipos de datos en Common Lisp incluyen:

Alcance

Al igual que los programas en muchos otros lenguajes de programación, los programas Common Lisp utilizan nombres para referirse a variables, funciones y muchos otros tipos de entidades. Las referencias nombradas están sujetas al alcance.

La asociación entre un nombre y la entidad a la que se refiere el nombre se denomina vinculación.

El alcance se refiere al conjunto de circunstancias en las que se determina que un nombre tiene una vinculación particular.

Determinantes del alcance

Las circunstancias que determinan el alcance en Common Lisp incluyen:

Para comprender a qué se refiere un símbolo, el programador de Common Lisp debe saber qué tipo de referencia se está expresando, qué tipo de alcance utiliza si es una referencia variable (alcance dinámico versus léxico) y también la situación en tiempo de ejecución: en qué entorno se resolvió la referencia, dónde se introdujo el enlace en el entorno, etcétera.

tipos de ambiente

Global

Algunos entornos en Lisp son globalmente omnipresentes. Por ejemplo, si se define un nuevo tipo, a partir de entonces se conocerá en todas partes. Las referencias a ese tipo lo buscan en este entorno global.

Dinámica

Un tipo de entorno en Common Lisp es el entorno dinámico. Los enlaces establecidos en este entorno tienen extensión dinámica, lo que significa que un enlace se establece al inicio de la ejecución de alguna construcción, como un letbloque, y desaparece cuando esa construcción termina de ejecutarse: su vida útil está ligada a la activación y desactivación dinámica de un bloque. Sin embargo, un enlace dinámico no sólo es visible dentro de ese bloque; también es visible para todas las funciones invocadas desde ese bloque. Este tipo de visibilidad se conoce como alcance indefinido. Se dice que los enlaces que exhiben extensión dinámica (vida útil ligada a la activación y desactivación de un bloque) y alcance indefinido (visible para todas las funciones que se llaman desde ese bloque) tienen alcance dinámico.

Common Lisp admite variables de ámbito dinámico, que también se denominan variables especiales. Ciertos otros tipos de enlaces también tienen necesariamente un alcance dinámico, como los reinicios y las etiquetas de captura. Los enlaces de funciones no pueden tener un alcance dinámico usando flet(que solo proporciona enlaces de funciones con un alcance léxico), pero los objetos de función (un objeto de primer nivel en Common Lisp) se pueden asignar a variables de alcance dinámico, vincularse usando leten un alcance dinámico y luego llamarse usando funcallo APPLY.

El alcance dinámico es extremadamente útil porque agrega claridad referencial y disciplina a las variables globales . Las variables globales están mal vistas en la informática como fuentes potenciales de error, porque pueden dar lugar a canales de comunicación encubiertos y ad hoc entre módulos que conducen a interacciones sorprendentes y no deseadas.

En Common Lisp, una variable especial que sólo tiene un enlace de nivel superior se comporta como una variable global en otros lenguajes de programación. Se puede almacenar un nuevo valor en él, y ese valor simplemente reemplaza lo que está en el enlace de nivel superior. La sustitución descuidada del valor de una variable global es la causa principal de los errores causados ​​por el uso de variables globales. Sin embargo, otra forma de trabajar con una variable especial es darle un enlace local nuevo dentro de una expresión. A esto a veces se le llama "volver a vincular" la variable. Vincular una variable de ámbito dinámico crea temporalmente una nueva ubicación de memoria para esa variable y asocia el nombre con esa ubicación. Mientras esa vinculación esté vigente, todas las referencias a esa variable se refieren a la nueva vinculación; el enlace anterior está oculto. Cuando finaliza la ejecución de la expresión de enlace, la ubicación de la memoria temporal desaparece y se revela el enlace anterior, con el valor original intacto. Por supuesto, se pueden anidar múltiples enlaces dinámicos para la misma variable.

En las implementaciones de Common Lisp que admiten subprocesos múltiples, los alcances dinámicos son específicos de cada subproceso de ejecución. Por tanto, las variables especiales sirven como abstracción para el almacenamiento local de subprocesos. Si un subproceso vuelve a vincular una variable especial, esta nueva vinculación no tiene ningún efecto sobre esa variable en otros subprocesos. El valor almacenado en un enlace solo puede ser recuperado por el hilo que creó ese enlace. Si cada hilo vincula alguna variable especial *x*, entonces *x*se comporta como un almacenamiento local de hilo. Entre los subprocesos que no se vuelven a vincular *x*, se comporta como un global ordinario: todos estos subprocesos se refieren al mismo enlace de nivel superior de *x*.

Las variables dinámicas se pueden utilizar para ampliar el contexto de ejecución con información de contexto adicional que se pasa implícitamente de una función a otra sin tener que aparecer como un parámetro de función adicional. Esto es especialmente útil cuando la transferencia de control tiene que pasar a través de capas de código no relacionado, que simplemente no se puede ampliar con parámetros adicionales para pasar datos adicionales. Una situación como esta normalmente requiere una variable global. Esa variable global debe guardarse y restaurarse, para que el esquema no se rompa durante la recursividad: la nueva vinculación dinámica de variables se encarga de esto. Y esa variable debe hacerse local al subproceso (o de lo contrario se debe usar un mutex grande) para que el esquema no se rompa bajo los subprocesos: las implementaciones de alcance dinámico también pueden encargarse de esto.

En la biblioteca Common Lisp, hay muchas variables especiales estándar. Por ejemplo, todos los flujos de E/S estándar se almacenan en enlaces de nivel superior de variables especiales conocidas. El flujo de salida estándar se almacena en *salida estándar*.

Supongamos que una función foo escribe en la salida estándar:

 ( defun foo () ( formato t "Hola mundo" ))     

Para capturar su salida en una cadena de caracteres, *salida estándar* puede vincularse a una secuencia de cadena y llamarse:

 ( con-salida-a-cadena ( *salida-estándar* ) ( foo ))  
-> "Hola mundo" ; salida recopilada devuelta como una cadena

Léxico

Common Lisp admite entornos léxicos. Formalmente, los enlaces en un entorno léxico tienen alcance léxico y pueden tener una extensión indefinida o una extensión dinámica, según el tipo de espacio de nombres. El alcance léxico significa que la visibilidad está restringida físicamente al bloque en el que se establece el enlace. Las referencias que no están textualmente (es decir, léxicamente) incrustadas en ese bloque simplemente no ven esa vinculación.

Las etiquetas en un TAGBODY tienen alcance léxico. La expresión (GO X) es errónea si no está incrustada en un TAGBODY que contiene una etiqueta X. Sin embargo, las vinculaciones de etiquetas desaparecen cuando el TAGBODY finaliza su ejecución, porque tienen extensión dinámica. Si ese bloque de código se vuelve a ingresar mediante la invocación de un cierre léxico , no es válido que el cuerpo de ese cierre intente transferir el control a una etiqueta a través de GO:

 ( defvar *escondido* ) ;; mantendrá una función   ( tagbody ( setf *stashed* ( lambda () ( ir a alguna etiqueta ))) ( ir a la etiqueta final ) ;; omitir (imprimir "Hola") alguna etiqueta ( imprimir " Hola " ) etiqueta final ) -> NULO               

Cuando se ejecuta TAGBODY, primero evalúa el formulario setf que almacena una función en la variable especial *escondida*. Luego (ir a la etiqueta final) transfiere el control a la etiqueta final, omitiendo el código (imprimir "Hola"). Dado que la etiqueta final está al final del cuerpo de la etiqueta, el cuerpo de la etiqueta termina, lo que produce NIL. Supongamos que la función previamente recordada ahora se llama:

 ( funcall *escondido* ) ;; ¡Error!  

Esta situación es errónea. La respuesta de una implementación es una condición de error que contiene el mensaje "GO: ya se ha dejado el cuerpo de la etiqueta para la etiqueta SOME-LABEL". La función intentó evaluar (ir a alguna etiqueta), que está incrustada léxicamente en el cuerpo de la etiqueta y se resuelve en la etiqueta. Sin embargo, el cuerpo de la etiqueta no se está ejecutando (su extensión ha finalizado) y, por lo tanto, la transferencia de control no puede realizarse.

Los enlaces de funciones locales en Lisp tienen alcance léxico y los enlaces de variables también tienen alcance léxico de forma predeterminada. A diferencia de las etiquetas GO, ambas tienen un alcance indefinido. Cuando se establece una función léxica o una vinculación variable, esa vinculación continúa existiendo mientras sean posibles las referencias a ella, incluso después de que la construcción que estableció esa vinculación haya terminado. Las referencias a variables y funciones léxicas después de la terminación de su constructo constituyente son posibles gracias a los cierres léxicos .

El enlace léxico es el modo de enlace predeterminado para las variables de Common Lisp. Para un símbolo individual, se puede cambiar al alcance dinámico, ya sea mediante una declaración local o mediante una declaración global. Esto último puede ocurrir implícitamente mediante el uso de una construcción como DEFVAR o DEFPARAMETER. Es una convención importante en la programación Common Lisp que las variables especiales (es decir, de alcance dinámico) tengan nombres que comiencen y terminen con un asterisco en * lo que se llama la " convención de orejeras ". [17] Si se cumple, esta convención crea efectivamente un espacio de nombres separado para variables especiales, de modo que las variables destinadas a ser léxicas no se conviertan accidentalmente en especiales.

El alcance léxico es útil por varias razones.

En primer lugar, las referencias a variables y funciones se pueden compilar en código de máquina eficiente, porque la estructura del entorno de ejecución es relativamente simple. En muchos casos, se puede optimizar para apilar el almacenamiento, por lo que abrir y cerrar ámbitos léxicos tiene una sobrecarga mínima. Incluso en los casos en los que se deben generar cierres completos, el acceso al entorno del cierre sigue siendo eficiente; normalmente, cada variable se convierte en un desplazamiento en un vector de enlaces, por lo que una referencia de variable se convierte en una simple instrucción de carga o almacenamiento con un modo de direccionamiento de base más desplazamiento .

En segundo lugar, el alcance léxico (combinado con la extensión indefinida) da lugar al cierre léxico , que a su vez crea todo un paradigma de programación centrado en el uso de funciones como objetos de primera clase, que está en la raíz de la programación funcional.

En tercer lugar, y quizás lo más importante, incluso si no se explotan los cierres léxicos, el uso del alcance léxico aísla los módulos de programa de interacciones no deseadas. Debido a su visibilidad restringida, las variables léxicas son privadas. Si un módulo A vincula una variable léxica X y llama a otro módulo B, las referencias a X en B no se resolverán accidentalmente en el X vinculado en A. B simplemente no tiene acceso a X. Para situaciones en las que las interacciones disciplinadas a través de una variable son deseable, Common Lisp proporciona variables especiales. Las variables especiales permiten que un módulo A establezca un enlace para una variable X que es visible para otro módulo B, llamado desde A. Poder hacer esto es una ventaja, y poder evitar que esto suceda también es una ventaja; en consecuencia, Common Lisp admite un alcance tanto léxico como dinámico .

macros

Una macro en Lisp se parece superficialmente a una función en su uso. Sin embargo, en lugar de representar una expresión que se evalúa, representa una transformación del código fuente del programa. La macro obtiene la fuente que rodea como argumentos, los vincula a sus parámetros y calcula una nueva forma de fuente. Este nuevo formulario también puede utilizar una macro. La expansión de la macro se repite hasta que el nuevo formulario fuente no utiliza una macro. La forma calculada final es el código fuente ejecutado en tiempo de ejecución.

Usos típicos de macros en Lisp:

También es necesario implementar varias funciones estándar de Common Lisp como macros, como por ejemplo:

Las macros están definidas por la macro defmacro . El macrolet de operador especial permite la definición de macros locales (de ámbito léxico). También es posible definir macros para símbolos usando define-symbol-macro y symbol-macrolet .

El libro de Paul Graham On Lisp describe en detalle el uso de macros en Common Lisp. El libro de Doug Hoyte Let Over Lambda amplía la discusión sobre las macros, afirmando que "las macros son la mayor ventaja que tiene lisp como lenguaje de programación y la mayor ventaja de cualquier lenguaje de programación". Hoyte proporciona varios ejemplos de desarrollo iterativo de macros.

Ejemplo de uso de una macro para definir una nueva estructura de control

Las macros permiten a los programadores de Lisp crear nuevas formas sintácticas en el lenguaje. Un uso típico es crear nuevas estructuras de control. La macro de ejemplo proporciona una untilconstrucción en bucle. La sintaxis es:

(hasta el formulario de prueba*)

La definición de macro para hasta :

( defmacro hasta ( prueba y cuerpo cuerpo ) ( let ( ( etiqueta de inicio ( gensym "INICIO " )) ( etiqueta final ( gensym " FIN " ))) ` ( cuerpo de etiqueta , etiqueta de inicio ( cuando , prueba ( ir , fin- etiqueta )) ( progn , @ cuerpo ) ( ir , etiqueta inicial ) , etiqueta final )))                      

tagbody es un operador especial primitivo de Common Lisp que brinda la capacidad de nombrar etiquetas y usar el formulario go para saltar a esas etiquetas. La cita invertida ` proporciona una notación que proporciona plantillas de código, donde se completa el valor de los formularios precedidos por una coma. Los formularios precedidos por una coma y un signo de arroba se empalman . El formulario del cuerpo de la etiqueta prueba la condición final. Si la condición es verdadera, salta a la etiqueta final. De lo contrario, se ejecuta el código del cuerpo proporcionado y luego salta a la etiqueta de inicio.

Un ejemplo del uso de lo anterior hasta la macro:

( hasta ( = ( aleatorio 10 ) 0 ) ( línea de escritura "Hola" ))      

El código se puede expandir usando la función macroexpand-1 . La expansión del ejemplo anterior se ve así:

( TAGBODY #:START1136 ( CUANDO ( ZEROP ( ALEATORIO 10 )) ( GO #:END1137 )) ( PROGN ( WRITE-LINE "hola " )) ( GO #:START1136 ) #:END1137 )             

Durante la expansión macro, el valor de la variable test es (= (random 10) 0) y el valor del cuerpo de la variable es ((write-line "Hello")) . El cuerpo es una lista de formas.

Los símbolos generalmente se ponen en mayúsculas automáticamente. La expansión utiliza TAGBODY con dos etiquetas. Los símbolos de estas etiquetas los calcula GENSYM y no están incluidos en ningún paquete. Los formularios de dos pasos utilizan estas etiquetas para saltar. Dado que tagbody es un operador primitivo en Common Lisp (y no una macro), no se expandirá a otra cosa. La forma expandida utiliza la macro when , que también se expandirá. Expandir completamente un formulario fuente se llama código caminando .

En la forma completamente expandida ( caminada ), la forma cuando se reemplaza por la primitiva si :

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

Todas las macros deben expandirse antes de que el código fuente que las contiene pueda evaluarse o compilarse normalmente. Las macros pueden considerarse funciones que aceptan y devuelven expresiones S , similares a los árboles de sintaxis abstracta , pero no limitadas a ellas. Estas funciones se invocan ante el evaluador o compilador para producir el código fuente final. Las macros están escritas en Common Lisp normal y pueden utilizar cualquier operador Common Lisp (o de terceros) disponible.

Captura variable y sombreado.

Las macros Lisp comunes son capaces de lo que comúnmente se llama captura de variables , donde los símbolos en el cuerpo de la macro expansión coinciden con los del contexto de llamada, lo que permite al programador crear macros en las que varios símbolos tienen un significado especial. El término captura de variables es algo engañoso, porque todos los espacios de nombres son vulnerables a capturas no deseadas, incluido el espacio de nombres del operador y la función, el espacio de nombres de la etiqueta del cuerpo de la etiqueta, la etiqueta de captura, el controlador de condiciones y los espacios de nombres de reinicio.

La captura de variables puede introducir defectos de software. Esto sucede de una de las dos maneras siguientes:

El dialecto Scheme de Lisp proporciona un sistema de macroescritura que proporciona la transparencia referencial que elimina ambos tipos de problemas de captura. Este tipo de macrosistema a veces se denomina "higiénico", en particular por sus defensores (que consideran antihigiénicos los macrosistemas que no resuelven automáticamente este problema). [ cita necesaria ]

En Common Lisp, la macrohigiene se garantiza de dos maneras diferentes.

Un enfoque es utilizar gensyms : símbolos únicos garantizados que se pueden utilizar en una macroexpansión sin amenaza de captura. El uso de gensyms en una definición de macro es una tarea manual, pero se pueden escribir macros que simplifiquen la creación de instancias y el uso de gensyms. Los gensyms resuelven fácilmente la captura de tipo 2, pero no son aplicables a la captura de tipo 1 de la misma manera, porque la macro expansión no puede cambiar el nombre de los símbolos que interfieren en el código circundante que captura sus referencias. Gensyms podría usarse para proporcionar alias estables para los símbolos globales que necesita la macro expansión. La expansión de la macro utilizaría estos alias secretos en lugar de los nombres conocidos, por lo que la redefinición de los nombres conocidos no tendría ningún efecto negativo en la macro.

Otro enfoque es utilizar paquetes. Una macro definida en su propio paquete puede simplemente usar símbolos internos en ese paquete en su expansión. El uso de paquetes se ocupa de la captura de tipo 1 y tipo 2.

Sin embargo, los paquetes no resuelven la captura tipo 1 de referencias a funciones y operadores estándar de Common Lisp. La razón es que el uso de paquetes para resolver problemas de captura gira en torno al uso de símbolos privados (símbolos en un paquete, que no se importan ni se hacen visibles en otros paquetes). Mientras que los símbolos de la biblioteca Common Lisp son externos y con frecuencia se importan o se hacen visibles en paquetes definidos por el usuario.

El siguiente es un ejemplo de captura no deseada en el espacio de nombres del operador, que ocurre en la expansión de una macro:

 ;; la expansión de UNTIL hace un uso liberal de DO ( defmacro hasta ( expresión & cuerpo cuerpo ) ` ( hacer () ( , expresión ) , @ cuerpo ))          ;; macrolet establece un enlace de operador léxico para DO ( macrolet (( do ( ... ) ... algo más ... )) ( hasta ( = ( aleatorio 10 ) 0 ) ( línea de escritura "Hola" )))              

La untilmacro se expandirá a una forma que llama doy que pretende hacer referencia a la macro estándar Common Lisp do. Sin embargo, en este contexto, dopuede tener un significado completamente diferente, por lo que untiles posible que no funcione correctamente.

Common Lisp resuelve el problema del sombreado de operadores y funciones estándar al prohibir su redefinición. Debido a que redefine el operador estándar do, lo anterior es en realidad un fragmento de Common Lisp no conforme, que permite que las implementaciones lo diagnostiquen y rechacen.

Sistema de condiciones

El sistema de condiciones es responsable del manejo de excepciones en Common Lisp. [18] Proporciona condiciones , controladores y reinicios . Las condiciones son objetos que describen una situación excepcional (por ejemplo, un error). Si se señala una condición , el sistema Common Lisp busca un controlador para este tipo de condición y llama al controlador. El controlador ahora puede buscar reinicios y utilizar uno de estos reinicios para reparar automáticamente el problema actual, utilizando información como el tipo de condición y cualquier información relevante proporcionada como parte del objeto de condición, y llamar a la función de reinicio adecuada.

Estos reinicios, si no están controlados por código, se pueden presentar a los usuarios (como parte de una interfaz de usuario, la de un depurador, por ejemplo), para que el usuario pueda seleccionar e invocar uno de los reinicios disponibles. Dado que el controlador de condiciones se llama en el contexto del error (sin desenrollar la pila), la recuperación completa del error es posible en muchos casos, donde otros sistemas de manejo de excepciones ya habrían terminado la rutina actual. El propio depurador también se puede personalizar o reemplazar utilizando la *debugger-hook*variable dinámica. El código que se encuentra dentro de formularios de protección de desenrollado, como los finalizadores, también se ejecutará según corresponda a pesar de la excepción.

En el siguiente ejemplo (usando Symbolics Genera ), el usuario intenta abrir un archivo en una prueba de función Lisp llamada desde Read-Eval-Print-LOOP ( REPL ), cuando el archivo no existe. El sistema Lisp presenta cuatro reinicios. El usuario selecciona Reintentar ABRIR usando un reinicio de nombre de ruta diferente e ingresa un nombre de ruta diferente (lispm-init.lisp en lugar de lispm-int.lisp). El código de usuario no contiene ningún código de manejo de errores. Todo el código de reinicio y manejo de errores lo proporciona el sistema Lisp, que puede manejar y reparar el error sin terminar el código de usuario.

Comando: (prueba ">zippy>lispm-int.lisp")Error: No se encontró el archivo. Para lispm:>zippy>lispm-int.lisp.newestLMFS: ABIERTO-LOCAL-LMFS-1 Arg 0: #P"lispm:>zippy>lispm-int.lisp.newest"sA, <Reanudar>: Reintentar ABRIR de lispm:>zippy>lispm-int.lisp.newestsB: Vuelva a intentar ABRIR usando un nombre de ruta diferenteSC, <Abortar>: Regresar al nivel superior de Lisp en un servidor TELNETsD: Reiniciar proceso terminal TELNET-> Reintentar ABRIR usando una ruta diferenteUtilice qué nombre de ruta en su lugar [lispm predeterminado:>zippy>lispm-int.lisp.newest]: lispm:>zippy>lispm-init.lisp.newest...el programa continúa

Sistema de objetos Lisp común (CLOS)

Common Lisp incluye un conjunto de herramientas para programación orientada a objetos , el Common Lisp Object System o CLOS . Peter Norvig explica cuántos patrones de diseño son más sencillos de implementar en un lenguaje dinámico con las características de CLOS (herencia múltiple, mixins, multimétodos, metaclases, combinaciones de métodos, etc.). [19] Se ha propuesto incluir varias extensiones de Common Lisp para programación orientada a objetos en el estándar ANSI Common Lisp, pero finalmente se adoptó CLOS como el sistema de objetos estándar para Common Lisp. CLOS es un sistema de objetos dinámico con despacho múltiple y herencia múltiple , y difiere radicalmente de las funciones de programación orientada a objetos que se encuentran en lenguajes estáticos como C++ o Java . Como sistema de objetos dinámicos, CLOS permite cambios en tiempo de ejecución a funciones y clases genéricas. Se pueden agregar y eliminar métodos, se pueden agregar y redefinir clases, se pueden actualizar objetos para cambios de clase y se puede cambiar la clase de objetos.

CLOS se ha integrado en ANSI Common Lisp. Las funciones genéricas se pueden utilizar como funciones normales y son un tipo de datos de primera clase. Cada clase CLOS está integrada en el sistema de tipos Common Lisp. Muchos tipos de Common Lisp tienen una clase correspondiente. Existe un mayor uso potencial de CLOS para Common Lisp. La especificación no dice si las condiciones se implementan con CLOS. Los nombres de rutas y flujos podrían implementarse con CLOS. Estas posibilidades de uso adicionales de CLOS para ANSI Common Lisp no forman parte del estándar. Las implementaciones reales de Common Lisp utilizan CLOS para nombres de rutas, flujos, entradas y salidas, condiciones, la implementación de CLOS en sí y más.

Compilador e intérprete

Un intérprete Lisp ejecuta directamente el código fuente Lisp proporcionado como objetos Lisp (listas, símbolos, números, ...) leídos a partir de expresiones-s. Un compilador Lisp genera código de bytes o código de máquina a partir del código fuente de Lisp. Common Lisp permite compilar en memoria funciones Lisp individuales y compilar archivos completos en código compilado almacenado externamente ( archivos fasl ).

Varias implementaciones de dialectos Lisp anteriores proporcionaron tanto un intérprete como un compilador. Lamentablemente, a menudo la semántica era diferente. Estos Lisps anteriores implementaron alcance léxico en el compilador y alcance dinámico en el intérprete. Common Lisp requiere que tanto el intérprete como el compilador utilicen el alcance léxico de forma predeterminada. El estándar Common Lisp describe tanto la semántica del intérprete como de un compilador. Se puede llamar al compilador usando la función compilar para funciones individuales y usando la función compilar-archivo para archivos. Common Lisp permite declaraciones de tipos y proporciona formas de influir en la política de generación de código del compilador. Para este último, a varias cualidades de optimización se les pueden dar valores entre 0 (no importante) y 3 (más importante): velocidad , espacio , seguridad , depuración y velocidad de compilación .

También hay una función para evaluar el código Lisp: eval. evaltoma el código como expresiones s previamente analizadas y no, como en otros idiomas, como cadenas de texto. De esta manera se puede construir código con las funciones Lisp habituales para construir listas y símbolos y luego este código se puede evaluar con la función eval. Varias implementaciones de Common Lisp (como Clozure CL y SBCL) se están implementando evalutilizando su compilador. De esta forma se compila el código, aunque se evalúa mediante la función eval.

El compilador de archivos se invoca mediante la función compilar-archivo . El archivo generado con código compilado se denomina archivo fasl (de carga rápida ). Estos archivos fasl y también los archivos de código fuente se pueden cargar con la función de carga en un sistema Common Lisp en ejecución. Dependiendo de la implementación, el compilador de archivos genera código de bytes (por ejemplo para la máquina virtual Java ), código en lenguaje C (que luego se compila con un compilador de C) o, directamente, código nativo.

Las implementaciones de Common Lisp se pueden utilizar de forma interactiva, aunque el código se compila por completo. Por lo tanto, la idea de un lenguaje interpretado no se aplica al Common Lisp interactivo.

El lenguaje hace una distinción entre tiempo de lectura, tiempo de compilación, tiempo de carga y tiempo de ejecución, y permite que el código de usuario también haga esta distinción para realizar el tipo de procesamiento deseado en el paso deseado.

Se proporcionan algunos operadores especiales para adaptarse especialmente al desarrollo interactivo; por ejemplo, defvarsolo asignará un valor a la variable proporcionada si aún no estaba vinculada, mientras que defparametersiempre realizará la asignación. Esta distinción es útil cuando se evalúa, compila y carga código de forma interactiva en una imagen en vivo.

También se proporcionan algunas características para ayudar a escribir compiladores e intérpretes. Los símbolos constan de objetos de primer nivel y son manipulables directamente mediante código de usuario. El progvoperador especial permite crear enlaces léxicos mediante programación, mientras que los paquetes también son manipulables. El compilador Lisp está disponible en tiempo de ejecución para compilar archivos o funciones individuales. Estos facilitan el uso de Lisp como compilador o intérprete intermedio para otro idioma.

Ejemplos de código

Paradoja del cumpleaños

El siguiente programa calcula el número más pequeño de personas en una habitación para las cuales la probabilidad de un cumpleaños único es inferior al 50% (la paradoja del cumpleaños , donde para 1 persona la probabilidad es obviamente del 100%, para 2 es 364/365, etc. ). La respuesta es 23.

En Common Lisp, por convención, las constantes están encerradas entre caracteres +.

( defconstante +año-tamaño+ 365 )  ( defun cumpleaños-paradoja ( probabilidad número de personas ) ( let (( nueva probabilidad ( * ( / ( - + tamaño del año + número de personas ) + tamaño del año + ) probabilidad ))) ( if ( < nuevo -probabilidad 0.5 ) ( 1+ número de personas ) ( paradoja-cumpleaños nueva-probabilidad ( 1+ número de personas )))))                      

Llamar a la función de ejemplo usando REPL (Read Eval Print Loop):

CL-USUARIO > (cumpleaños-paradoja 1.0 1)23

Ordenar una lista de objetos persona

Definimos una clase persony un método para mostrar el nombre y la edad de una persona. A continuación definimos un grupo de personas como una lista de personobjetos. Luego iteramos sobre la lista ordenada.

( defclass persona () (( nombre :initarg :nombre :accessor nombre-persona ) ( edad :initarg :edad :accessor persona-edad )) ( :documentation "La clase PERSONA con espacios NOMBRE y EDAD." ))              ( visualización defmethod (( objeto persona ) flujo ) "Mostrar un objeto PERSONA en un flujo de salida." ( con ranuras ( nombre edad ) objeto ( formato flujo "~a (~a)" nombre edad )))              ( defparameter *grupo* ( list ( make-instance 'persona :nombre "Bob" :edad 33 ) ( make-instance 'persona :nombre "Chris" :edad 16 ) ( make-instance 'persona :nombre "Ash" :edad 23 )) "Una lista de objetos PERSONA. "                     ( dolist ( persona ( ordenar ( lista de copias *grupo* ) #' > :clave #' edad-persona )) ( mostrar persona *salida-estándar* ) ( terpri ))           

Imprime los tres nombres con edad descendente.

Bob (33)Ceniza (23)cris (16)

Exponenciar al cuadrado

Se demuestra el uso de la macro LOOP:

( defun power ( x n ) ( bucle con resultado = 1 while ( plusp n ) cuando ( oddp n ) do ( setf result ( * result x )) do ( setf x ( * x x ) n ( truncate n 2 )) finalmente ( devolver resultado )))                                 

Uso de ejemplo:

CL-USUARIO > ( potencia 2 200 ) 1606938044258990275541962092341162602522202993782792835301376    

Compare con la exponenciación incorporada:

CL-USUARIO > ( = ( expt 2 200 ) ( potencia 2 200 )) T        

Encuentra la lista de shells disponibles

CON-OPEN-FILE es una macro que abre un archivo y proporciona una secuencia. Cuando regresa el formulario, el archivo se cierra automáticamente. FUNCALL llama a un objeto de función. El LOOP recopila todas las líneas que coinciden con el predicado.

( defun list-matching-lines ( predicado de archivo ) "Devuelve una lista de líneas en el archivo, para las cuales el predicado aplicado a la línea devuelve T." ( with-open-file ( archivo continuo ) ( bucle para línea = ( lectura- flujo de línea nil nil ) mientras que la línea cuando ( línea de predicado funcall ) recogerlo )))                       

La función DISPONIBLE-SHELLS llama a la función anterior LIST-MATCHING-LINES con un nombre de ruta y una función anónima como predicado. El predicado devuelve el nombre de ruta de un shell o NIL (si la cadena no es el nombre de archivo de un shell).

( defun shells disponibles ( &opcional ( archivo #p"/etc/shells " )) ( archivo de líneas coincidentes de lista ( lambda ( línea ) ( y ( plusp ( línea de longitud )) ( char = ( línea de caracteres 0 ) #\ / ) ( nombre de ruta ( string-right-trim ' ( #\space #\tab ) línea ))))))                      

Resultados de ejemplo (en Mac OS X 10.6):

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

Comparación con otros Lisps

Common Lisp se compara y contrasta con mayor frecuencia con Scheme , aunque sólo sea porque son los dos dialectos Lisp más populares. Scheme es anterior a CL y proviene no sólo de la misma tradición de Lisp sino de algunos de los mismos ingenieros: Guy Steele , con quien Gerald Jay Sussman diseñó Scheme, presidió el comité de estándares de Common Lisp.

Common Lisp es un lenguaje de programación de propósito general, a diferencia de las variantes de Lisp como Emacs Lisp y AutoLISP , que son lenguajes de extensión integrados en productos particulares (GNU Emacs y AutoCAD, respectivamente). A diferencia de muchos Lisps anteriores, Common Lisp (como Scheme ) utiliza un alcance de variable léxica de forma predeterminada tanto para el código interpretado como para el compilado.

La mayoría de los sistemas Lisp cuyos diseños contribuyeron a Common Lisp, como ZetaLisp y Franz Lisp, utilizaron variables de ámbito dinámico en sus intérpretes y variables de ámbito léxico en sus compiladores. Scheme introdujo el uso exclusivo de variables de ámbito léxico en Lisp; una inspiración de ALGOL 68 . CL también admite variables de ámbito dinámico, pero deben declararse explícitamente como "especiales". No existen diferencias en el alcance entre los intérpretes y compiladores de ANSI CL.

Common Lisp a veces se denomina Lisp-2 y Scheme a Lisp-1 , en referencia al uso que hace CL de espacios de nombres separados para funciones y variables. (De hecho, CL tiene muchos espacios de nombres, como los de etiquetas go, nombres de bloques y looppalabras clave). Existe una controversia de larga data entre CL y los defensores de Scheme sobre las compensaciones involucradas en múltiples espacios de nombres. En Scheme, es (en general) necesario evitar dar nombres a las variables que entren en conflicto con las funciones; Las funciones de esquema frecuentemente tienen argumentos llamados lis, lsto lystpara no entrar en conflicto con la función del sistema list. Sin embargo, en CL es necesario hacer referencia explícita al espacio de nombres de la función al pasar una función como argumento, lo cual también es algo común, como en el sortejemplo anterior.

CL también se diferencia de Scheme en su manejo de valores booleanos. Scheme utiliza los valores especiales #t y #f para representar la verdad y la falsedad. CL sigue la antigua convención Lisp de usar los símbolos T y NIL, donde NIL también representa la lista vacía. En CL, cualquier valor que no sea NIL se trata como verdadero mediante condicionales, como if, mientras que en Scheme todos los valores que no son #f se tratan como verdaderos. Estas convenciones permiten que algunos operadores en ambos lenguajes sirvan como predicados (respondiendo a una pregunta con valor booleano) y como retorno de un valor útil para cálculos posteriores, pero en Scheme el valor '() que es equivalente a NIL en Common Lisp se evalúa como verdadero. en una expresión booleana.

Por último, los documentos de estándares de Scheme requieren una optimización final , lo que el estándar CL no requiere. La mayoría de las implementaciones de CL ofrecen optimización de llamadas finales, aunque a menudo sólo cuando el programador utiliza una directiva de optimización. No obstante, el estilo de codificación CL común no favorece el uso ubicuo de la recursividad que prefiere el estilo Scheme: lo que un programador de Scheme expresaría con recursividad de cola, un usuario de CL normalmente lo expresaría con una expresión iterativa en do, dolist, loopo (más recientemente) con el iteratepaquete.

Implementaciones

Consulte la categoría Implementaciones de Common Lisp .

Common Lisp se define mediante una especificación (como Ada y C ) en lugar de una implementación (como Perl ). Hay muchas implementaciones y las áreas de detalles estándar en las que pueden diferir válidamente.

Además, las implementaciones tienden a venir con extensiones que brindan funciones no cubiertas por el estándar:

Se han creado bibliotecas de software gratuitas y de código abierto para admitir extensiones de Common Lisp de forma portátil, y se encuentran principalmente en los repositorios de Common-Lisp.net [20] y CLOCC (Common Lisp Open Code Collection) [21 ] proyectos.

Las implementaciones comunes de Lisp pueden utilizar cualquier combinación de compilación de código nativo, compilación o interpretación de código de bytes. Common Lisp ha sido diseñado para admitir compiladores incrementales , compiladores de archivos y compiladores de bloques. En la especificación del lenguaje se proponen declaraciones estándar para optimizar la compilación (como la inserción de funciones o la especialización de tipos). La mayoría de las implementaciones de Common Lisp compilan el código fuente en código de máquina nativo . Algunas implementaciones pueden crear aplicaciones independientes (optimizadas). Otros compilan en código de bytes interpretado , que es menos eficiente que el código nativo, pero facilita la portabilidad del código binario. Algunos compiladores compilan código Common Lisp en código C. La idea errónea de que Lisp es un lenguaje puramente interpretado se debe probablemente a que los entornos Lisp proporcionan un mensaje interactivo y ese código se compila uno por uno, de forma incremental. Con Common Lisp la compilación incremental se utiliza ampliamente.

Algunas implementaciones basadas en Unix ( CLIP , SBCL ) se pueden utilizar como lenguaje de secuencias de comandos ; es decir, invocado por el sistema de forma transparente como lo hace un intérprete de shell de Perl o Unix . [22]

Lista de implementaciones

Implementaciones comerciales

Ceceo común de Allegro
para Microsoft Windows, FreeBSD, Linux, Apple macOS y varias variantes de UNIX. Allegro CL proporciona un entorno de desarrollo integrado (IDE) (para Windows y Linux) y amplias capacidades para la entrega de aplicaciones.
Ceceo común líquido
anteriormente llamado Lucid Common Lisp. Sólo mantenimiento, no nuevos lanzamientos.
LispWorks
para Microsoft Windows, FreeBSD, Linux, Apple macOS, iOS, Android y varias variantes de UNIX. LispWorks proporciona un entorno de desarrollo integrado (IDE) (disponible para la mayoría de las plataformas, pero no para iOS y Android) y amplias capacidades para la entrega de aplicaciones.
mocl
para iOS, Android y macOS.
Géneros abiertos
para DEC Alfa.
Lisp común científico
que está diseñado para informática científica de alto rendimiento.

Implementaciones libremente redistribuibles

Ceceo común del oso armado (ABCL)
Una implementación CL que se ejecuta en la máquina virtual Java . [23] Incluye un compilador de código de bytes de Java y permite el acceso a bibliotecas de Java desde CL. Anteriormente era solo un componente del Editor Armed Bear J.
Corchete
Una implementación basada en LLVM que interopera perfectamente con bibliotecas de C++. Se ejecuta en varios sistemas Unix y similares (incluido macOS ).
CLIP
Una implementación de compilación de código de bytes, portátil y que se ejecuta en varios sistemas Unix y similares (incluido macOS ), así como en Microsoft Windows y varios otros sistemas.
Clozure CL (CCL)
Originalmente una bifurcación gratuita y de código abierto de Macintosh Common Lisp. Como lo implica esa historia, CCL fue escrito para Macintosh, pero Clozure CL ahora se ejecuta en macOS , FreeBSD , Linux , Solaris y Windows . Cada plataforma admite puertos x86 de 32 y 64 bits . Además, hay puertos Power PC para Mac OS y Linux. CCL se conocía anteriormente como OpenMCL, pero ese nombre ya no se usa para evitar confusión con la versión de código abierto de Macintosh Common Lisp.
CMUCL
Originario de la Universidad Carnegie Mellon , ahora mantenido como software gratuito y de código abierto por un grupo de voluntarios. CMUCL utiliza un compilador rápido de código nativo. Está disponible en Linux y BSD para Intel x86; Linux para Alfa; macOS para Intel x86 y PowerPC; y Solaris, IRIX y HP-UX en sus plataformas nativas.
Ceceo común de Corman
para Microsoft Windows. En enero de 2015, Corman Lisp se publicó bajo licencia del MIT. [24]
Lisp común integrable (ECL)
ECL incluye un intérprete y compilador de código de bytes. También puede compilar código Lisp en código máquina a través de un compilador C. Luego, ECL compila el código Lisp en C, compila el código C con un compilador de C y luego puede cargar el código de máquina resultante. También es posible incrustar ECL en programas C y código C en programas Common Lisp.
Lisp común de GNU (GCL)
El compilador Lisp del Proyecto GNU . Sin embargo, GCL aún no es totalmente compatible con ANSI, pero es la implementación elegida para varios proyectos grandes, incluidas las herramientas matemáticas Maxima , AXIOM y (históricamente) ACL2 . GCL se ejecuta en Linux bajo once arquitecturas diferentes, y también bajo Windows, Solaris y FreeBSD .
Lisp común de Macintosh (MCL)
La versión 5.2 para computadoras Apple Macintosh con un procesador PowerPC que ejecuta Mac OS X es de código abierto. RMCL (basado en MCL 5.2) se ejecuta en computadoras Apple Macintosh basadas en Intel utilizando el traductor binario Rosetta de Apple.
Lisp común de ManKai (MKCL)
Una rama de ECL . MKCL enfatiza la confiabilidad, la estabilidad y la calidad general del código a través de un sistema de tiempo de ejecución nativo de subprocesos múltiples y muy reelaborado. En Linux, MKCL presenta un sistema de ejecución totalmente compatible con POSIX.
Movitz
Implementa un entorno Lisp para computadoras x86 sin depender de ningún sistema operativo subyacente.
Registro pop
Poplog implementa una versión de CL, con POP-11 y, opcionalmente, Prolog y Standard ML (SML), lo que permite la programación en lenguaje mixto. Para todos, el lenguaje de implementación es POP-11, que se compila de forma incremental. También tiene un editor integrado similar a Emacs que se comunica con el compilador.
Lisp común del banco de acero (SBCL)
Una sucursal de CMUCL . "En términos generales, SBCL se diferencia de CMU CL por un mayor énfasis en la mantenibilidad". [25] SBCL se ejecuta en las plataformas CMUCL, excepto HP/UX; además, corre en Linux para AMD64, PowerPC, SPARC, MIPS, Windows x86 y AMD64. [26] SBCL no utiliza un intérprete por defecto; todas las expresiones se compilan en código nativo a menos que el usuario active el intérprete. El compilador SBCL genera código nativo rápido según una versión anterior de The Computer Language Benchmarks Game . [27]
Lisp común de Ufasoft
Puerto de CLISP para plataforma Windows con núcleo escrito en C++.

Otras implementaciones

Ceceo común de Austin y Kioto
una evolución de Kyoto Common Lisp por Bill Schelter
Ceceo común de mariposa
una implementación escrita en Scheme para la computadora multiprocesador BBN Butterfly [28] [29]
CLIC
un compilador común de Lisp a C [30]
CLOE
Lisp común para PC de Symbolics
Lisp común de Codemist
utilizado para la versión comercial del sistema de álgebra informática Axiom [31] [32]
Lisp común experto
una implementación temprana para Apple Macintosh por ExperTelligence
Ceceo común dorado
una implementación para PC de GoldHill Inc. [33] [34]
Ceceo común de Ibuki
una versión comercializada de Kyoto Common Lisp
Ceceo común de Kioto
el primer compilador Common Lisp que utilizó C como lenguaje de destino. GCL, ECL y MKCL se originan a partir de esta implementación de Common Lisp.
l
una versión pequeña de Common Lisp para sistemas integrados desarrollada por IS Robotics, ahora iRobot [35]
Máquinas Lisp (de Symbolics , TI [36] [37] y Xerox [38] )
proporcionó implementaciones de Common Lisp además de su dialecto Lisp nativo (Lisp Machine Lisp o Interlisp). CLOS también estaba disponible. Symbolics proporciona una versión mejorada de Common Lisp. [39] [40] [41]
Ceceo común de Procyon
una implementación para Windows y Mac OS, utilizada por Franz para su versión Windows de Allegro CL
LISP común de zafiro estrella
una implementación para la PC
subl
una variante de Common Lisp utilizada para la implementación del sistema basado en conocimiento Cyc [42]
Lisp común de nivel superior
una implementación temprana para ejecución concurrente [43]
WCL
una implementación de biblioteca compartida [44] [45]
Ceceo común VAX
Implementación de Digital Equipment Corporation que se ejecutó en sistemas VAX que ejecutan VMS o ULTRIX
XLISP
una implementación escrita por David Betz [46]

Aplicaciones

Common Lisp se utiliza para desarrollar aplicaciones de investigación (a menudo en Inteligencia Artificial ), para el desarrollo rápido de prototipos o para aplicaciones implementadas.

Common Lisp se utiliza en muchas aplicaciones comerciales, incluido Yahoo! Sitio de comercio web de la tienda, que originalmente involucraba a Paul Graham y luego fue reescrito en C++ y Perl . [47] Otros ejemplos notables incluyen:

También existen aplicaciones de código abierto escritas en Common Lisp, como:

Ver también

Referencias

  1. ^ "Acción sobre estándares ANSI - 28 de diciembre de 2018" (PDF) . ansi.org . Archivado (PDF) desde el original el 12 de abril de 2021.
  2. ^ Citado de la portada de la norma citada. ANSI INCITS 226-1994 [S2008], a la venta en la página del documento estándar Archivado el 27 de septiembre de 2020 en Wayback Machine .
  3. ^ "CLHS: Acerca de Common Lisp HyperSpec (TM)". lispworks.com .
  4. ^ "CLHS: Sección 1.1.2". lispworks.com .
  5. ^ "Implementaciones comunes de Lisp: una encuesta". Archivado desde el original el 21 de abril de 2012 . Consultado el 22 de diciembre de 2007 .
  6. ^ "Los antiguos programas LISP todavía se ejecutan en Common Lisp" . Consultado el 13 de mayo de 2015 .
  7. ^ "Raíces de" Yu-Shiang Lisp ", correo de Jon L White, 1982". cmu.edu .
  8. ^ "Índice de correo". cl-su-ai.lisp.se .
  9. ^ Anti-LOOPismo instintivo y otros fenómenos del correo electrónico: patrones orales, escritos y electrónicos en la comunicación mediada por computadora, JoAnne Yates y Wanda J. Orlikowski., 1993 Archivado el 8 de agosto de 2012 en Wayback Machine .
  10. ^ Steele, Guy L. Jr. (15 de agosto de 1982). "Una descripción general de COMMON LISP". Actas del simposio ACM de 1982 sobre LISP y programación funcional - LFP '82 . ACM. págs. 98-107. doi :10.1145/800068.802140. ISBN 9780897910828. S2CID  14517358.
  11. ^ Reddy, Abhishek (22 de agosto de 2008). "Características de Common Lisp".
  12. ^ "Soporte Unicode". La Wiki de Common Lisp . Consultado el 21 de agosto de 2008 .
  13. ^ Richard P. Gabriel; Kent M. Pitman (junio de 1988). "Cuestiones técnicas de separación en celdas de función y celdas de valor". LISP y Computación Simbólica . 1 (1): 81–101. doi :10.1007/bf01806178. S2CID  26716515.
  14. ^ "Hyperspec de Common Lisp: Sección 3.1.7".
  15. ^ "Common Lisp Hyperspec: función PISO".
  16. ^ "Common Lisp Hyperspec: accesorio GETHASH".
  17. ^ "Dejar pasar Lambda". letoverlambda.com .
  18. ^ Peter Seibel (7 de abril de 2005). Ceceo común práctico. Presione. ISBN 978-1-59059-239-7.
  19. ^ "Patrones de diseño en programación dinámica". norvig.com .
  20. ^ "Common-Lisp.net".
  21. ^ "Colección de código abierto Common Lisp".
  22. ^ "32.6. Entrega de inicio rápido con CLISP". clip.cons.org .
  23. ^ "Cceceo común del oso armado".
  24. ^ "Las fuentes de Corman Lisp ya están disponibles". 5 de enero de 2015.
  25. ^ "Historia y derechos de autor". Lisp común de Steel Bank .
  26. ^ "Mesa de plataforma". Lisp común de Steel Bank .
  27. ^ "¿Qué programas son más rápidos? - Juego de pruebas de lenguaje informático". 20 de mayo de 2013. Archivado desde el original el 20 de mayo de 2013.
  28. ^ "Paquete: lang/lisp/impl/bbn/". cs.cmu.edu .
  29. ^ "Desarrollos recientes en Butterfly Lisp, 1987, actas de la AAAI" (PDF) . aaai.org . Archivado (PDF) desde el original el 11 de octubre de 2015.
  30. ^ Burkart, O.; Goerigk, W.; Knutzen, H. (22 de junio de 1992). "CLICC: un nuevo enfoque para la compilación de programas Lisp comunes en C". CiteSeerX 10.1.1.38.1282 .  {{cite journal}}: Citar diario requiere |journal=( ayuda )
  31. ^ "codemist.co.uk". lisp.codemist.co.uk .
  32. ^ "Axioma, el horizonte de 30 años, página 43" (PDF) .
  33. ^ "Desarrollador Golden Common Lisp". goldhill-inc.com .
  34. ^ Golden Common LISP: un enfoque práctico, David J. Steele, junio de 2000 por Addison Wesley Publishing Company
  35. ^ Brooks, Rodney A.; al., et (22 de junio de 1995). "L - Un Lisp común para sistemas integrados". CiteSeerX 10.1.1.2.1953 .  {{cite journal}}: Citar diario requiere |journal=( ayuda )
  36. ^ "Conceptos de programación de TI Explorer" (PDF) .
  37. ^ "Referencia de TI Explorer Lisp" (PDF) .
  38. ^ "Notas de la versión de Medley Lisp" (PDF) .
  39. ^ "Diccionario de Lisp común simbólico" (PDF) . trailing-edge.com . Archivado (PDF) desde el original el 22 de abril de 2015.
  40. ^ "Conceptos simbólicos del lenguaje Lisp común" (PDF) . trailing-edge.com . Archivado (PDF) desde el original el 22 de abril de 2015.
  41. ^ "Construcciones de programación Common Lisp simbólicas" (PDF) . trailing-edge.com . Archivado (PDF) desde el original el 22 de abril de 2015.
  42. ^ "Referencia SubL - Cycorp". cyc.com .
  43. ^ "Top Level Inc. - Grupo de preservación de software". softwarepreservation.org .
  44. ^ WCL: Entrega de aplicaciones Common Lisp eficientes en Unix, Actas de la conferencia ACM de 1992 sobre LISP y programación funcional, páginas 260–269
  45. ^ "commonlisp.net :: WCL". pgc.com . Archivado desde el original el 5 de abril de 2016 . Consultado el 25 de marzo de 2016 .
  46. ^ "Paquete: lang/lisp/impl/xlisp/". cs.cmu.edu .
  47. ^ "Superando los promedios". paulgraham.com .
  48. ^ "Asistente del autor" (PDF) . aaai.org . Archivado (PDF) desde el original el 4 de junio de 2011.
  49. ^ Asistente del autorizador de American Express Archivado el 12 de diciembre de 2009 en Wayback Machine .
  50. Desarrollo de aplicaciones en tiempo real Archivado el 2 de agosto de 2016 en Wayback Machine . Gensim. Consultado el 16 de agosto de 2016.
  51. ^ "Genworks GDL".
  52. ^ "Opusmodus - Inicio".
  53. PWGL – Inicio Archivado el 3 de mayo de 2011 en Wayback Machine , obtenido el 17 de julio de 2013.
  54. ^ ab "Aeroespacial: Common Lisp". lisp-lang.org .
  55. ^ "Usuarios de piano, recuperado de la página del fabricante".
  56. ^ "Grammarly.com, ejecución de Lisp en producción". 26 de junio de 2015.
  57. ^ "Agente remoto". ti.arc.nasa.gov .
  58. ^ "Cceceo en JPL".
  59. ^ "Aplicaciones de clientes de Franz Inc: NASA". franz.com .
  60. ^ Sistema de programación y planificación de picos. Stsci.edu. Consultado el 17 de julio de 2013.
  61. ^ "Aplicaciones de clientes de Franz Inc: Space Telescope Institute". franz.com .
  62. ^ "Cómo empezó todo... También conocido como el nacimiento de CLR". microsoft.com . 28 de agosto de 2023.
  63. ^ Huffman, Steve. "en ceceo". Votado a favor . Archivado desde el original el 17 de mayo de 2018 . Consultado el 11 de mayo de 2019 .
  64. ^ "Cargador de páginas".
  65. ^ "¿Por qué pgloader es mucho más rápido?". 14 de mayo de 2014.

Bibliografía

Una lista cronológica de libros publicados (o a punto de publicarse) sobre Common Lisp (el lenguaje) o sobre programación con Common Lisp (especialmente programación con IA).

enlaces externos