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] (anteriormente X3.226-1994 (R1999) del American National Standards Institute (ANSI) . [2] Common Lisp HyperSpec , una versión HTML con hipervínculos , se ha derivado 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 extender las características de estos dialectos de MacLisp. Common Lisp no es una implementación, sino más bien una especificación de lenguaje . [4] Hay disponibles varias implementaciones del estándar Common Lisp, incluido software libre 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 eficientes en tiempo de ejecución. Este desarrollo incremental a menudo se realiza de forma interactiva sin interrumpir la aplicación en ejecución.

También admite la 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 un código más eficiente. Por ejemplo, fixnumpuede contener un entero sin encapsular en un rango compatible con el hardware y la implementación, lo que permite una aritmética más eficiente que con enteros grandes o tipos de precisión arbitraria. De manera similar, se le puede indicar al compilador, por módulo o por función, qué tipo de nivel de seguridad se desea, mediante declaraciones de optimización .

Common Lisp incluye CLOS , un sistema de objetos que admite métodos múltiples y combinaciones de métodos. Suele implementarse con un protocolo de metaobjetos .

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

Common Lisp ofrece compatibilidad parcial con versiones anteriores de Maclisp y del Lisp original de John McCarthy . Esto permite que el software Lisp más antiguo se pueda trasladar a Common Lisp. [6]

Historia

El trabajo sobre Common Lisp comenzó en 1981 tras una iniciativa del director de ARPA, Bob Engelmore, para desarrollar un dialecto Lisp estándar para la comunidad. [7] Gran parte del diseño inicial del lenguaje se realizó mediante correo electrónico. [8] [9] En 1982, Guy L. Steele Jr. presentó 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 del lenguaje 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 ANSI Common Lisp: sintaxis LOOP extendida, el Common Lisp Object System, el Condition System para el manejo de errores, una interfaz para la impresora bonita 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. Varias extensiones y mejoras a Common Lisp (por ejemplo, Unicode, Concurrency, IO basado en CLOS) han sido proporcionadas por implementaciones y bibliotecas .

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, los formularios de macros y los 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 como resultado 4. El nombre de la función es '+'. Lisp no tiene operadores como tales.   
 ( defvar *x* ) ; Asegura que existe una variable *x*, ; sin darle un valor. Los asteriscos son parte del ; nombre, que por convención denota una variable especial (global). ; El símbolo *x* también está dotado con la propiedad de que ; las vinculaciones subsiguientes son dinámicas, en lugar de léxicas. ( setf *x* 42.1 ) ; Establece la variable *x* en el valor de punto flotante 42.1          
 ;; Define una función que eleva al cuadrado un número: ( defun square ( x ) ( * x x ))      
 ;; Ejecuta la función: ( cuadrado 3 ) ; Devuelve 9   
 ;; La construcción 'let' crea un ámbito para las variables locales. Aquí ;; la variable 'a' está asociada a 6 y la variable 'b' está asociada ;; a 4. Dentro de 'let' hay un 'cuerpo', donde se devuelve el último valor calculado. ;; Aquí se devuelve el resultado de sumar a y b desde la expresión 'let'. ;; Las variables a y b tienen ámbito léxico, a menos que los símbolos hayan sido ;; marcados como variables especiales (por ejemplo, mediante un DEFVAR anterior). ( let (( a 6 ) ( b 4 )) ( + a b )) ; devuelve 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 números grandes para representar valores numéricos de tamaño y precisión arbitrarios. El tipo de proporción representa fracciones de manera exacta, una función que no está disponible en muchos lenguajes. Common Lisp convierte automáticamente los valores numéricos entre estos tipos según corresponda.

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

El tipo de símbolo es común en los lenguajes Lisp, pero en gran medida desconocido fuera de ellos. Un símbolo es un objeto de datos único y nombrado con varias partes: nombre, valor, función, lista de propiedades y paquete. De estos, la celda de valor y la celda de función son las más importantes. Los símbolos en Lisp se utilizan a menudo de forma 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 en la palabra clave package son autoevaluables. Los valores booleanos en Common Lisp se representan mediante los símbolos autoevaluables 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 entero más cercano, y los casos intermedios se redondean al entero par. Las funciones truncate, floory ceilingredondean hacia cero, hacia abajo o hacia arriba respectivamente. Todas estas funciones devuelven la parte fraccionaria descartada como un valor secundario. Por ejemplo, (floor -2.5)da como resultado −3, 0,5; (ceiling -2.5)da como resultado −2, −0,5; (round 2.5)da como resultado 2, 0,5; y (round 3.5)da como resultado 4, −0,5.

Estructuras de datos

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

Como en casi todos los demás dialectos de Lisp, las listas en Common Lisp se componen de conses , a veces llamadas celdas o pares de cons . Un cons es una estructura de datos con dos ranuras, llamadas car y cdr . Una lista es una cadena enlazada de conses o la lista vacía. El car de cada cons hace referencia a un miembro de la lista (posiblemente otra lista). El cdr de cada cons hace referencia al siguiente cons, excepto el último cons de una lista, cuyo cdr hace referencia al nilvalor. Los conses también se pueden usar fácilmente para implementar árboles y otras estructuras de datos complejas; aunque normalmente se recomienda usar 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 redimensionar dinámicamente matrices ajustables si es necesario. Las matrices multidimensionales se pueden utilizar para matemáticas matriciales. Un vector es una matriz unidimensional. Las matrices pueden tener cualquier tipo como miembros (incluso tipos mixtos en la misma matriz) o pueden especializarse para contener un tipo específico de miembros, como en un vector de bits. Por lo general, solo se admiten unos pocos tipos. Muchas implementaciones pueden optimizar las funciones de matriz cuando la matriz utilizada está especializada en tipos. Dos tipos de matriz especializados 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 utilizarse como clave o valor. Las tablas hash se redimensionan 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 y marcarlos como parte de una interfaz pública. Los paquetes pueden utilizar otros paquetes.

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

Las clases son similares a las estructuras, pero ofrecen características más dinámicas y herencia múltiple (consulte CLOS ). 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 tanto funciones como 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 estas funciones de orden superior. Por ejemplo, la sortfunción toma un operador relacional como argumento y la función clave como argumento de palabra clave opcional. Esto se puede utilizar no solo para ordenar cualquier tipo de datos, sino también para ordenar estructuras de datos según una clave.

 ;; Ordena la lista usando la función > y < como operador relacional. ( sort ( list 5 2 6 3 1 4 ) #' > ) ; Devuelve (6 5 4 3 2 1) ( sort ( list 5 2 6 3 1 4 ) #' < ) ; Devuelve (1 2 3 4 5 6)                    
 ;; Ordena la lista según el primer elemento de cada sublista. ( sort ( list ' ( 9 A ) ' ( 3 B ) ' ( 4 C )) #' < :key #' first ) ; Devuelve ((3 B) (4 C) (9 A))            

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

  1. Un operador especial (fácil de comprobar con una lista fija)
  2. Un operador macro (debe haber sido definido previamente)
  3. El nombre de una función (predeterminado), que puede ser un símbolo o una subforma 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 y se invoca con esos valores suministrados como parámetros.

Definición de 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 un cuerpo de la función:

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

Las definiciones de funciones pueden incluir directivas del compilador , conocidas como declaraciones , que proporcionan pistas al compilador sobre las configuraciones 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 square ( x ) "Calcula el cuadrado del punto flotante simple x." ( declare ( punto flotante simple x ) ( optimized ( speed 3 ) ( debug 0 ) ( safety 1 ))) ( el punto flotante simple ( * 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 que resulta útil proporcionar funciones anónimas como argumentos.

Las funciones locales se pueden definir con flety labels.

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

Existen otros operadores relacionados con la definición y manipulación de funciones. Por ejemplo, una función puede compilarse con el compileoperador. (Algunos sistemas Lisp ejecutan funciones utilizando un intérprete de forma predeterminada, a menos que se les indique que las 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 de 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.

 ( defgeneric añadir ( a b ))   
 ( defmethod add (( un número ) ( b número )) ( + a b ))        
 ( defmethod add (( a vector ) ( b número )) ( map 'vector ( lambda ( n ) ( + n b )) a ))             
 ( defmethod add (( a vector ) ( b vector )) ( map 'vector #' + a b ))          
( defmethod add (( una cadena ) ( b cadena )) ( concatenar 'cadena a b ))         
 ( suma 2 3 ) ; devuelve 5 ( suma #( 1 2 3 4 ) 7 ) ; devuelve #(8 9 10 11) ( suma #( 1 2 3 4 ) #( 4 3 2 1 )) ; devuelve #(5 5 5 5) ( suma "COMÚN " "LISP" ) ; devuelve "LISP COMÚN"                        

Las funciones genéricas también son un tipo de datos de primera clase . Las funciones y los métodos genéricos tienen muchas más características que las descritas anteriormente.

El espacio de nombres de la función

El espacio de nombres para los nombres de funciones es independiente del espacio de nombres para las variables de datos. Esta es una diferencia clave entre Common Lisp y Scheme . En Common Lisp, los operadores que definen nombres en el espacio de nombres de funciones incluyen defun, flet, labels, defmethody defgeneric.

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 utilizarí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 del 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 , que podrían causar problemas en Scheme, ya que ocultarían localmente los nombres de las funciones.

En la comunidad Lisp, el debate sobre si un espacio de nombres independiente para las funciones es una ventaja o no es motivo de controversia. Suele hablarse de este debate como el debate Lisp-1 vs. Lisp-2 . Lisp-1 hace referencia al modelo de Scheme y Lisp-2 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 múltiples valores [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 los llamadores interesados. 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 los llamadores pueden permanecer completamente inconscientes de la existencia de los valores secundarios 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-valores-múltiples ( cociente resto ) ( truncar x y ) ( formato nil "~A dividido por ~A es ~A resto ~A" x y cociente resto )))                 ;;;; => "1266778 dividido por 458 es 2765 resto 408"
( defun get-answer ( biblioteca ) ( gethash 'respuesta biblioteca 42 ))      ( defun the-answer-1 ( biblioteca ) ( formato nil "La respuesta es ~A" ( biblioteca get-answer ))) ;;;; Devuelve "La respuesta es 42" si RESPUESTA no está presente en BIBLIOTECA       ( defun the-answer-2 ( biblioteca ) ( enlace-de-valores-múltiples ( respuesta seguro-p ) ( obtener-respuesta biblioteca ) ( if ( no seguro-p ) "No sé" ( formato nil "La respuesta es ~A" respuesta )))) ;;;; Devuelve "No sé" si RESPUESTA no está presente en 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" ( values ​​"Perspectiva buena" ( random 1.0 )))       ;;;; => "Perspectiva buena" ;;;; => 0.3187

Otros tipos

Otros tipos de datos en Common Lisp incluyen:

Alcance

Al igual que los programas de muchos otros lenguajes de programación, los programas Common Lisp utilizan nombres para hacer referencia a variables, funciones y muchos otros tipos de entidades. Las referencias con nombre están sujetas a un alcance.

La asociación entre un nombre y la entidad a la que el nombre hace referencia se denomina enlace.

El alcance se refiere al conjunto de circunstancias en las que se determina que un nombre tiene un vínculo particular.

Determinantes del alcance

Las circunstancias que determinan el alcance en Common Lisp incluyen:

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

Tipos de ambiente

Global

Algunos entornos de Lisp son de alcance global. Por ejemplo, si se define un nuevo tipo, se conocerá en todas partes a partir de entonces. 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 una extensión dinámica, lo que significa que un enlace se establece al comienzo de la ejecución de algún constructo, como un letbloque, y desaparece cuando ese constructo termina de ejecutarse: su duración está ligada a la activación y desactivación dinámica de un bloque. Sin embargo, un enlace dinámico no solo 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 presentan una extensión dinámica (duración ligada a la activación y desactivación de un bloque) y un alcance indefinido (visible para todas las funciones que se invocan desde ese bloque) tienen alcance dinámico.

Common Lisp admite variables con alcance dinámico, también llamadas variables especiales. Algunos otros tipos de enlaces también tienen necesariamente un alcance dinámico, como las etiquetas de reinicio y de captura. Los enlaces de funciones no pueden tener un alcance dinámico usando flet(que solo proporciona enlaces de funciones con alcance léxico), pero los objetos de función (un objeto de primer nivel en Common Lisp) pueden asignarse a variables con alcance dinámico, vincularse usando leten el 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 posibles fuentes de error, porque pueden dar lugar a canales de comunicación encubiertos y ad hoc entre módulos que conducen a interacciones no deseadas y sorprendentes.

En Common Lisp, una variable especial que solo tiene un enlace de nivel superior se comporta igual que una variable global en otros lenguajes de programación. Se puede almacenar un nuevo valor en ella y ese valor simplemente reemplaza lo que está en el enlace de nivel superior. El reemplazo descuidado 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 nuevo enlace local dentro de una expresión. Esto a veces se conoce como "reenlazar" la variable. La vinculación de una variable con ámbito dinámico crea temporalmente una nueva ubicación de memoria para esa variable y asocia el nombre con esa ubicación. Mientras ese enlace está en efecto, todas las referencias a esa variable se refieren al nuevo enlace; el enlace anterior se oculta. Cuando finaliza la ejecución de la expresión de enlace, la ubicación de 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 ámbitos dinámicos son específicos de cada subproceso de ejecución. Por lo tanto, las variables especiales sirven como una abstracción para el almacenamiento local de subprocesos. Si un subproceso vuelve a vincular una variable especial, esta revinculación no tiene efecto sobre esa variable en otros subprocesos. El valor almacenado en una vinculación solo puede ser recuperado por el subproceso que creó esa vinculación. Si cada subproceso vincula alguna variable especial *x*, entonces *x*se comporta como almacenamiento local de subprocesos. Entre los subprocesos que no vuelven a vincular *x*, se comporta como un global ordinario: todos estos subprocesos hacen referencia a la misma vinculación 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 por capas de código no relacionado, que simplemente no se pueden ampliar con parámetros adicionales para pasar los datos adicionales. Una situación como esta suele requerir una variable global. Esa variable global se debe guardar y restaurar, de modo que el esquema no se rompa bajo la recursión: la revinculación de variables dinámicas se encarga de esto. Y esa variable debe hacerse local para el subproceso (o de lo contrario se debe utilizar un mutex grande) para que el esquema no se rompa bajo los subprocesos: las implementaciones de ámbito 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 los enlaces de nivel superior de variables especiales conocidas. El flujo de salida estándar se almacena en *standard-output*.

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, *standard-output* se puede vincular a un flujo de cadena y llamar:

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

Léxico

Common Lisp admite entornos léxicos. Formalmente, los enlaces en un entorno léxico tienen un alcance léxico y pueden tener una extensión indefinida o 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 ese enlace.

Las etiquetas de 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, los enlaces de etiquetas desaparecen cuando el TAGBODY finaliza su ejecución, porque tienen alcance dinámico. 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 *stashed* ) ;; contendrá una función   ( tagbody ( setf *stashed* ( lambda () ( go some-label ))) ( go end-label ) ;; omitir (imprimir "Hola") some-label ( imprimir "Hola" ) end-label ) -> NIL               

Cuando se ejecuta TAGBODY, primero evalúa la forma setf que almacena una función en la variable especial *stashed*. Luego (go end-label) transfiere el control a end-label, salteándose el código (print "Hello"). Dado que end-label está al final del cuerpo de la etiqueta, este termina, dando como resultado NIL. Supongamos que ahora se llama a la función recordada anteriormente:

 ( 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: el cuerpo de etiqueta para la etiqueta SOME-LABEL ya se ha dejado". La función intentó evaluar (go some-label), que está léxicamente incrustado en el cuerpo de etiqueta, y se resuelve en la etiqueta. Sin embargo, el cuerpo de etiqueta no se está ejecutando (su extensión ha finalizado), por lo que la transferencia de control no puede tener lugar.

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

La vinculación léxica es el modo de vinculación predeterminado para las variables de Common Lisp. Para un símbolo individual, se puede cambiar a un ámbito 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 de Common Lisp que las variables especiales (es decir, de ámbito dinámico) tengan nombres que comiencen y terminen con un asterisco, en * lo que se denomina la " convención de las orejeras ". [17] Si se respeta, esta convención crea efectivamente un espacio de nombres separado para las variables especiales, de modo que las variables que se pretende que sean léxicas no se conviertan en especiales accidentalmente.

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 el almacenamiento en pila, 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, y así 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 que son objetos de primera clase, que es 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 ámbito léxico aísla los módulos del 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 límite de X en A. B simplemente no tiene acceso a X. Para situaciones en las que son deseables las interacciones disciplinadas a través de una variable, Common Lisp proporciona variables especiales. Las variables especiales permiten que un módulo A establezca un enlace para una variable X que sea visible para otro módulo B, llamado desde A. Poder hacer esto es una ventaja, y poder evitar que suceda también es una ventaja; en consecuencia, Common Lisp admite tanto el ámbito léxico como el dinámico .

Macros

En Lisp, una macro se parece superficialmente a una función. 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 el código fuente que rodea como argumentos, los vincula a sus parámetros y calcula una nueva forma de código fuente. Esta nueva forma también puede utilizar una macro. La expansión de la macro se repite hasta que la nueva forma de código fuente no utilice una macro. La forma calculada final es el código fuente que se ejecuta en tiempo de ejecución.

Usos típicos de macros en Lisp:

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

Las macros se definen mediante la macro defmacro . El operador especial macrolet permite la definición de macros locales (con alcance léxico). También es posible definir macros para símbolos mediante 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 el debate 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 de bucle. La sintaxis es:

(hasta el formato de prueba*)

La definición de macro para hasta :

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

tagbody es un operador especial primitivo de Common Lisp que proporciona la capacidad de nombrar etiquetas y usar la forma go para saltar a esas etiquetas. La comilla invertida ` proporciona una notación que proporciona plantillas de código, donde se completa el valor de las formas precedidas por una coma. Las formas precedidas por una coma y un signo arroba se insertan . La forma tagbody 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 inicial.

Un ejemplo del uso de la macro hasta anterior :

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

El código se puede expandir utilizando 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 de la macro, el valor de la variable test es (= (random 10) 0) y el valor de la variable body es ((write-line "Hello")) . El cuerpo es una lista de formularios.

Los símbolos se convierten automáticamente en mayúsculas y minúsculas. La expansión utiliza TAGBODY con dos etiquetas. Los símbolos para estas etiquetas son calculados por GENSYM y no se almacenan en ningún paquete. Dos formas go utilizan estas etiquetas para saltar a ellas. 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á. La expansión completa de una forma fuente se denomina " code walking" .

En la forma completamente expandida ( walked ), la forma when se reemplaza por la primitiva if :

( TAGBODY #:START1136 ( IF ( ZEROP ( RANDOM 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 ellos. Estas funciones se invocan antes que el evaluador o compilador para producir el código fuente final. Las macros se escriben en Common Lisp normal y pueden usar cualquier operador de Common Lisp (o de terceros) disponible.

Captura variable y sombreado

Las macros de Common Lisp son capaces de realizar lo que comúnmente se denomina captura de variables , donde los símbolos en el cuerpo de la expansión de la macro 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 un tanto engañoso, porque todos los espacios de nombres son vulnerables a la captura no deseada, incluidos los espacios de nombres de operadores y funciones, los espacios de nombres de etiquetas de cuerpo de etiqueta, etiquetas catch, manejadores de condiciones y espacios de nombres de reinicio.

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

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

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

Un enfoque es utilizar gensyms : símbolos garantizados únicos que pueden utilizarse 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 instanciación y el uso de gensyms. Los gensyms resuelven la captura de tipo 2 fácilmente, pero no son aplicables a la captura de tipo 1 de la misma manera, porque la macroexpansión no puede cambiar el nombre de los símbolos que interfieren en el código circundante que capturan sus referencias. Los gensyms se pueden utilizar para proporcionar alias estables para los símbolos globales que necesita la macroexpansión. La macroexpansión 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 el uso de paquetes. Una macro definida en su propio paquete puede simplemente usar símbolos internos de 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 de 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 ) ` ( do ( ) ( , expresión ) ,@ cuerpo ))          ;; macrolet establece la vinculación del operador léxico para DO ( macrolet (( do ( ... ) ... algo más ... )) ( hasta que ( = ( aleatorio 10 ) 0 ) ( línea de escritura "Hola" )))              

La untilmacro se expandirá a una forma que invoque dolo que pretende hacer referencia a la macro estándar de 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, lo que permite que las implementaciones lo diagnostiquen y lo rechacen.

Sistema de condiciones

El sistema de condiciones es responsable del manejo de excepciones en Common Lisp. [18] Proporciona condiciones , manejadores 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 manejador para este tipo de condición y llama al manejador. El manejador ahora puede buscar reinicios y usar uno de estos reinicios para reparar automáticamente el problema actual, usando 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 apropiada.

Estos reinicios, si no son controlados por código, pueden presentarse a los usuarios (como parte de una interfaz de usuario, la de un depurador, por ejemplo), de modo 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 depurador en sí también puede personalizarse o reemplazarse utilizando la *debugger-hook*variable dinámica. El código que se encuentra dentro de los 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 (utilizando 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 Retry OPEN utilizando una ruta de reinicio diferente e ingresa una ruta de reinicio 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 manejo de errores y reinicio 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 Argumento 0: #P"lispm:>zippy>lispm-int.lisp.newest"sA, <Reanudar>: Reintentar ABRIR lispm:>zippy>lispm-int.lisp.newestsB: Vuelva a intentar ABRIR utilizando una ruta diferentesC, <Abort>: Regresar al nivel superior de Lisp en un servidor TELNETsD: Reiniciar proceso terminal TELNET-> Reintentar ABRIR usando una ruta diferenteUtilice la ruta de acceso en su lugar [lispm predeterminado:>zippy>lispm-int.lisp.newest]: lispm:>zippy>lispm-init.lisp.más nuevo...el programa continúa

Sistema de objetos Common Lisp (CLOS)

Common Lisp incluye un conjunto de herramientas para programación orientada a objetos , el Common Lisp Object System o CLOS . Peter Norvig explica cómo muchos patrones de diseño son más simples de implementar en un lenguaje dinámico con las características de CLOS (herencia múltiple, mixins, métodos múltiples, metaclases, combinaciones de métodos, etc.). [19] Se han propuesto varias extensiones de Common Lisp para programación orientada a objetos para incluirlas en el estándar ANSI Common Lisp, pero finalmente CLOS se adoptó como el sistema de objetos estándar para Common Lisp. CLOS es un sistema de objetos dinámico con envío múltiple y herencia múltiple , y difiere radicalmente de las facilidades OOP que se encuentran en lenguajes estáticos como C++ o Java . Como sistema de objetos dinámico, CLOS permite cambios en tiempo de ejecución en 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 Common Lisp tienen una clase correspondiente. Hay más uso potencial de CLOS para Common Lisp. La especificación no dice si las condiciones se implementan con CLOS. Los nombres de ruta y los flujos se podrían implementar con CLOS. Estas posibilidades de uso adicionales de CLOS para ANSI Common Lisp no son parte del estándar. Las implementaciones reales de Common Lisp utilizan CLOS para nombres de ruta, flujos, entrada-salida, condiciones, la implementación de CLOS en sí y más.

Compilador e intérprete

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

Varias implementaciones de dialectos Lisp anteriores proporcionaban tanto un intérprete como un compilador. Desafortunadamente, a menudo la semántica era diferente. Estos Lisp anteriores implementaban el alcance léxico en el compilador y el 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 la de un compilador. El compilador puede ser llamado utilizando la función compile para funciones individuales y utilizando la función compile-file 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, se pueden dar valores entre 0 (no importante) y 3 (el más importante) a varias cualidades de optimización: speed , space , safety , debug y compilation-speed .

También hay una función para evaluar código Lisp: eval. evaltoma el código como expresiones s preanalizadas y no, como en otros lenguajes, como cadenas de texto. De esta manera, el código se puede construir 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 implementan evalutilizando su compilador. De esta manera, el código se compila, aunque se evalúe utilizando la función eval.

El compilador de archivos se invoca mediante la función compile-file . El archivo generado con el código compilado se denomina archivo fasl (de fast load ). Estos archivos fasl y también los archivos de código fuente se pueden cargar con la función load en un sistema Common Lisp en ejecución. Según la implementación, el compilador de archivos genera código de bytes (por ejemplo, para la máquina virtual de 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 compile por completo. Por lo tanto, la idea de un lenguaje interpretado no se aplica a 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 su variable proporcionada si no estaba ya 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 funciones para ayudar a escribir compiladores e intérpretes. Los símbolos consisten en objetos de primer nivel y son manipulables directamente por el código del usuario. El progvoperador especial permite crear enlaces léxicos mediante programación, mientras que los paquetes también son manipulables. El compilador de Lisp está disponible en tiempo de ejecución para compilar archivos o funciones individuales. Esto facilita el uso de Lisp como compilador o intérprete intermedio para otro lenguaje.

Ejemplos de código

Paradoja del cumpleaños

El siguiente programa calcula el número más pequeño de personas en una sala para las cuales la probabilidad de cumpleaños únicos es menor del 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 se encierran entre caracteres +.

( defconstant + tamaño-año+ 365 )  ( defun paradoja-de-cumpleaños ( probabilidad numero-de-personas ) ( let (( nueva-probabilidad ( * ( / ( - +año-tamaño+ numero-de-personas ) +año-tamaño+ ) probabilidad ))) ( if ( < nueva-probabilidad 0.5 ) ( 1+ numero-de-personas ) ( paradoja-de-cumpleaños nueva-probabilidad ( 1+ numero-de-personas )))))                      

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

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

Ordenar una lista de objetos de 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 person () (( name :initarg :name :accessor person-name ) ( age :initarg :age :accessor person-age )) ( :documentation "La clase PERSONA con los espacios NOMBRE y EDAD." ))              ( defmethod display (( object person ) stream ) "Mostrando un objeto PERSONA en un flujo de salida." ( with-slots ( name age ) object ( format stream "~a (~a)" name age )))              ( defparameter *group* ( list ( make-instance 'person :name "Bob" :age 33 ) ( make-instance 'person :name "Chris" :age 16 ) ( make-instance 'person :name "Ash" :age 23 )) "Una lista de objetos PERSONA." )                     ( dolist ( persona ( sort ( lista-de-copias *grupo* ) #' > :key #' edad-de-persona )) ( mostrar persona *salida-estándar* ) ( terpri ))           

Imprime los tres nombres con edad descendente.

Bob (33)Ceniza (23)Cristóbal (16)

Exponenciando por cuadrado

Se demuestra el uso de la macro LOOP:

( defun potencia ( x n ) ( bucle con resultado = 1 mientras ( plusp n ) cuando ( oddp n ) hacer ( setf resultado ( * resultado x )) hacer ( setf x ( * x x ) n ( truncar n 2 )) finalmente ( devolver resultado )))                                 

Ejemplo de uso:

CL-USUARIO > ( potencia 2 200 ) 1606938044258990275541962092341162602522202993782792835301376    

Compárese con la exponenciación incorporada:

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

Encuentra la lista de conchas disponibles

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

( defun list-matching-lines ( file predicate ) "Devuelve una lista de líneas en el archivo, para las cuales el predicado aplicado a la línea devuelve T." ( with-open-file ( stream file ) ( loop for line = ( read-line stream nil nil ) while line when ( funcall predicate line ) collect it )))                       

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

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

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 Lisp

Common Lisp se compara y contrasta con Scheme con mucha frecuencia , aunque sólo sea porque son los dos dialectos de Lisp más populares. Scheme es anterior a CL y no sólo proviene de la misma tradición de Lisp, sino también 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 específicos (GNU Emacs y AutoCAD, respectivamente). A diferencia de muchos Lisp anteriores, Common Lisp (como Scheme ) utiliza el alcance de la variable léxica de manera 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) utilizaban variables con ámbito dinámico en sus intérpretes y variables con ámbito léxico en sus compiladores. Scheme introdujo el uso exclusivo de variables con ámbito léxico en Lisp; una inspiración de ALGOL 68. CL también admite variables con ámbito dinámico, pero deben declararse explícitamente como "especiales". No existen diferencias en el ámbito entre los intérpretes y compiladores ANSI CL.

A Common Lisp se le denomina a veces Lisp-2 y a Scheme Lisp-1 , en referencia al uso de CL de espacios de nombres separados para funciones y variables. (De hecho, CL tiene muchos espacios de nombres, como los de las etiquetas go, nombres de bloques y looppalabras clave). Existe una controversia de larga data entre los defensores de CL y Scheme sobre las compensaciones involucradas en múltiples espacios de nombres. En Scheme, es (ampliamente) necesario evitar dar nombres de variables que entren en conflicto con las funciones; las funciones de Scheme con frecuencia tienen argumentos llamados lis, lst, o 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 cuando se pasa una función como argumento, lo que también es una ocurrencia común, como en el sortejemplo anterior.

CL también difiere de Scheme en su manejo de valores booleanos. Scheme usa los valores especiales #t y #f para representar la verdad y la falsedad. CL sigue la antigua convención de 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 sean #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 optimización de llamadas de cola , algo que el estándar CL no hace. La mayoría de las implementaciones de CL ofrecen optimización de llamadas de cola, aunque a menudo solo cuando el programador usa una directiva de optimización. No obstante, el estilo de codificación CL común no favorece el uso ubicuo de la recursión que prefiere el estilo Scheme: lo que un programador de Scheme expresaría con recursión de cola, un usuario 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 ). Existen muchas implementaciones y el estándar detalla áreas en las que pueden diferir válidamente.

Además, las implementaciones tienden a venir con extensiones, que proporcionan funcionalidad no cubierta en el estándar:

Se han creado bibliotecas de software libre y de código abierto para soportar extensiones de Common Lisp de manera portable, y se encuentran principalmente en los repositorios de los proyectos Common-Lisp.net [20] y CLOCC (Common Lisp Open Code Collection) [21] .

Las implementaciones de Common Lisp pueden usar cualquier combinación de compilación de código nativo, compilación de código de bytes o interpretación. Common Lisp ha sido diseñado para soportar compiladores incrementales , compiladores de archivos y compiladores de bloques. Las declaraciones estándar para optimizar la compilación (como la inserción de funciones en línea o la especialización de tipos) se proponen en la especificación del lenguaje. La mayoría de las implementaciones de Common Lisp compilan el código fuente a código de máquina nativo . Algunas implementaciones pueden crear aplicaciones independientes (optimizadas). Otras compilan a 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 el código de Common Lisp a código C. La idea errónea de que Lisp es un lenguaje puramente interpretado es más probable porque 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 usa ampliamente.

Algunas implementaciones basadas en Unix ( CLISP , SBCL ) se pueden utilizar como lenguaje de script , es decir, el sistema las puede invocar de forma transparente, de la misma forma que lo hace un intérprete de shell de Perl o Unix . [22]

Lista de implementaciones

Implementaciones comerciales

Allegro Ceceo común
Para Microsoft Windows, FreeBSD, Linux, Apple macOS y varias variantes de UNIX. Allegro CL ofrece un entorno de desarrollo integrado (IDE) (para Windows y Linux) y amplias capacidades para la distribución de aplicaciones.
Lisp común líquido
Anteriormente se llamaba Lucid Common Lisp. Solo mantenimiento, no hay nuevas versiones.
LispWorks
Para Microsoft Windows, FreeBSD, Linux, Apple macOS, iOS, Android y varias variantes de UNIX. LispWorks ofrece 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 distribución de aplicaciones.
moclobem
para iOS, Android y macOS.
Abrir géneros
para DEC Alpha.
Ciencia Common Lisp
que está diseñado para la computación científica de alto rendimiento.

Implementaciones libremente redistribuibles

Oso armado con ceceo común (ABCL)
Una implementación de 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 J de Armed Bear.
Corchete
Una implementación basada en LLVM que interopera sin problemas con bibliotecas 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 a Unix (incluido macOS ), así como en Microsoft Windows y varios otros sistemas.
Clozure CL (CCL)
Originalmente era una bifurcación libre y de código abierto de Macintosh Common Lisp. Como lo indica la historia, CCL fue escrito para Macintosh, pero Clozure CL ahora se ejecuta en macOS , FreeBSD , Linux , Solaris y Windows . Se admiten puertos x86 de 32 y 64 bits en cada plataforma. 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 confusiones con la versión de código abierto de Macintosh Common Lisp.
CMUCL
Originalmente creado por la Universidad Carnegie Mellon , ahora mantenido como software libre y de código abierto por un grupo de voluntarios. CMUCL utiliza un compilador de código nativo rápido. Está disponible en Linux y BSD para Intel x86; Linux para Alpha; macOS para Intel x86 y PowerPC; y Solaris, IRIX y HP-UX en sus plataformas nativas.
Corman Ceceo común
Para Microsoft Windows. En enero de 2015 se publicó Corman Lisp bajo licencia MIT. [24]
Common Lisp integrable (ECL)
ECL incluye un intérprete de bytecode y un compilador. También puede compilar código Lisp a código de máquina mediante un compilador de C. A continuación, ECL compila el código Lisp a C, compila el código C con un compilador de C y, a continuación, puede cargar el código de máquina resultante. También es posible incorporar ECL en programas C y código C en programas Common Lisp.
GNU Common Lisp (GCL)
El compilador Lisp del Proyecto GNU . Aunque aún no es totalmente compatible con ANSI, GCL es sin embargo la implementación preferida 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 en Windows, Solaris y FreeBSD .
Lisp común de Macintosh (MCL)
La versión 5.2 para ordenadores Apple Macintosh con procesador PowerPC que ejecuten Mac OS X es de código abierto. RMCL (basado en MCL 5.2) se ejecuta en ordenadores Apple Macintosh con procesador Intel que utilizan el traductor binario Rosetta de Apple.
Lenguaje de programación Common Lisp (MKCL) de ManKai
Una rama de ECL . MKCL enfatiza la confiabilidad, la estabilidad y la calidad general del código a través de un sistema de ejecución multiproceso nativo y altamente rediseñado. En Linux, MKCL cuenta con un sistema de ejecución totalmente compatible con POSIX.
Movimentación
Implementa un entorno Lisp para computadoras x86 sin depender de ningún sistema operativo subyacente.
Poplog
Poplog implementa una versión de CL, con POP-11 y, opcionalmente, Prolog y Standard ML (SML), lo que permite la programación en lenguajes mixtos. 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.
Banco de acero Common Lisp (SBCL)
Una rama de CMUCL . "En términos generales, SBCL se distingue de CMU CL por un mayor énfasis en la mantenibilidad". [25] SBCL se ejecuta en las plataformas que CMUCL, excepto HP/UX; además, se ejecuta en Linux para AMD64, PowerPC, SPARC, MIPS, Windows x86 y AMD64. [26] SBCL no utiliza un intérprete de forma predeterminada; 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 de acuerdo con una versión anterior de The Computer Language Benchmarks Game . [27]
Lisp común de Ufasoft
Puerto de CLISP para la plataforma Windows con núcleo escrito en C++.

Otras implementaciones

Austin Kioto Lisp común
Una evolución de Kyoto Common Lisp por Bill Schelter
Mariposa ceceo común
una implementación escrita en Scheme para la computadora multiprocesador BBN Butterfly [28] [29]
CLIC
Un compilador de Common Lisp a C [30]
CLOÉ
Common Lisp para PC de Symbolics
Codemist Lisp común
utilizado para la versión comercial del sistema de álgebra computacional Axiom [31] [32]
Experto en Lisp común
Una implementación temprana para Apple Macintosh por ExperTelligence
Ceceo común dorado
Una implementación para PC de GoldHill Inc. [33] [34]
Ibuki Ceceo común
una versión comercializada de Kyoto Common Lisp
Lisp común de Kioto
el primer compilador Common Lisp que utilizó C como lenguaje de destino. GCL, ECL y MKCL se originaron a partir de esta implementación de Common Lisp.
yo
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] )
Se proporcionaron implementaciones de Common Lisp además de su dialecto nativo de Lisp (Lisp Machine Lisp o Interlisp). CLOS también estaba disponible. Symbolics proporciona una versión mejorada de Common Lisp. [39] [40] [41]
Procyon común lisp
Una implementación para Windows y Mac OS, utilizada por Franz para su puerto Windows de Allegro CL
Zafiro estrellado LISP común
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]
CMT
una implementación de biblioteca compartida [44] [45]
VAX Lisp común
Implementación de Digital Equipment Corporation que se ejecutó en sistemas VAX que ejecutaban VMS o ULTRIX
XLIP
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 el sitio de comercio web Yahoo! Store, que originalmente involucró 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:

Véase también

Referencias

  1. ^ "Acción de las normas ANSI - 28 de diciembre de 2018" (PDF) . ansi.org . Archivado (PDF) del 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 de la norma 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 de Common Lisp: una encuesta". Archivado desde el original el 21 de abril de 2012. Consultado el 22 de diciembre de 2007 .
  6. ^ "Los programas LISP antiguos todavía funcionan 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. Número de identificación del sujeto  14517358.
  11. ^ Reddy, Abhishek (22 de agosto de 2008). "Características de Common Lisp".
  12. ^ "Compatibilidad con Unicode". 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. ^ "Hiperespecificación de Common Lisp: Sección 3.1.7".
  15. ^ "Hiperespecificación Common Lisp: Función FLOOR".
  16. ^ "Hiperespecificación Common Lisp: Accesor GETHASH".
  17. ^ "Deja pasar a Lambda". letoverlambda.com .
  18. ^ Peter Seibel (7 de abril de 2005). Practical Common Lisp. Apress. ISBN 978-1-59059-239-7.
  19. ^ "Patrones de diseño en programación dinámica". norvig.com .
  20. ^ "Lisp común.net".
  21. ^ "Colección de código abierto Common Lisp".
  22. ^ "32.6. Inicio rápido de la entrega con CLISP". clisp.cons.org .
  23. ^ "Oso armado ceceo común".
  24. ^ "Las fuentes de Corman Lisp ya están disponibles". 5 de enero de 2015.
  25. ^ "Historia y derechos de autor". Steel Bank Common Lisp .
  26. ^ "Mesa de plataforma". Steel Bank Common Lisp .
  27. ^ "¿Cuáles son los programas más rápidos? – Juego de evaluación comparativa de lenguajes informáticos". 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. ^ "Recent Developments in Butterfly Lisp, 1987, AAAI Proceedings" (PDF) . aaai.org . Archivado (PDF) del 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 Common Lisp en C". CiteSeerX 10.1.1.38.1282 .  {{cite journal}}: Requiere citar revista |journal=( ayuda )
  31. ^ "codemist.co.uk". lisp.codemist.co.uk .
  32. ^ "Axioma, el horizonte de 30 años, página 43" (PDF) .
  33. ^ "Desarrollador de 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}}: Requiere citar revista |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 símbolos de Common Lisp" (PDF) . trailing-edge.com . Archivado (PDF) del original el 22 de abril de 2015.
  40. ^ "Conceptos simbólicos del lenguaje Common Lisp" (PDF) . trailing-edge.com . Archivado (PDF) del original el 22 de abril de 2015.
  41. ^ "Constructos simbólicos de programación en Common Lisp" (PDF) . trailing-edge.com . Archivado (PDF) del 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: Cómo ofrecer 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) del 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 . Gensym. 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 , consultado 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. ^ "Ceceo en el JPL".
  59. ^ "Aplicaciones para clientes de Franz Inc: NASA". franz.com .
  60. ^ Sistema de planificación y programación de Spike. Stsci.edu. Consultado el 17 de julio de 2013.
  61. ^ "Aplicaciones para clientes de Franz Inc: Instituto del Telescopio Espacial". franz.com .
  62. ^ "Cómo empezó todo... o sea, el nacimiento del CLR". microsoft.com . 28 de agosto de 2023.
  63. ^ Huffman, Steve. "on lisp". 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 de IA).

Enlaces externos