Lisp comum

Da Wikipédia, a enciclopédia livre
Ir para a navegação Saltar para pesquisar

Lisp comum
ParadigmaMultiparadigma : processual , funcional , orientado a objetos , meta , reflexivo , genérico
FamíliaLisp
Projetado porScott Fahlman , Richard P. Gabriel , David A. Moon , Kent Pitman , Guy Steele , Dan Weinreb
DesenvolvedorComitê ANSI X3J13
Apareceu pela primeira vez1984 (38 anos atrás) , 1994 (28 anos atrás) para ANSI Common Lisp ( 1984 ) ( 1994 )
Disciplina de digitaçãoDinâmico , forte
AlcanceLexical, opcionalmente dinâmico
SOMultiplataforma
Extensões de nome de arquivo.lisp, .lsp, .l, .cl, .fasl
Local na rede Internetcommon-lisp .net
Principais implementações
Allegro CL , ABCL , CLISP , Clozure CL , CMUCL , ECL , GCL , LispWorks , Scieneer CL , SBCL , Symbolics Common Lisp
Dialetos
CLtL1, CLtL2, ANSI Common Lisp
Influenciado por
Lisp , Lisp Machine Lisp , Maclisp , Scheme , Interlisp
Influenciado
Clojure , Dylan , Emacs Lisp , EuLisp , ISLISP , *Lisp , AutoLisp , Julia , Moose , R , SKILL , SubL

Common Lisp ( CL ) é um dialeto da linguagem de programação Lisp , publicado no documento padrão ANSI ANSI INCITS 226-1994 (S20018) [1] (anteriormente X3.226-1994 (R1999) ). [2] O Common Lisp HyperSpec , uma versão HTML com hiperlink, foi derivado do padrão ANSI Common Lisp. [3]

A linguagem Common Lisp foi desenvolvida como um sucessor padronizado e aprimorado do Maclisp . No início da década de 1980, vários grupos já estavam trabalhando em diversos sucessores do MacLisp: Lisp Machine Lisp (também conhecido como ZetaLisp), Spice Lisp , NIL e S-1 Lisp . Common Lisp procurou unificar, padronizar e estender os recursos desses dialetos MacLisp. Common Lisp não é uma implementação, mas sim uma especificação de linguagem . [4] Várias implementações do padrão Common Lisp estão disponíveis, incluindo software livre e de código aberto e produtos proprietários. [5] Common Lisp é um software de uso geral,linguagem de programação multiparadigma . Ele suporta uma combinação de paradigmas de programação procedural , funcional e orientado a objetos . Como uma linguagem de programação dinâmica , facilita o desenvolvimento de software evolutivo e incremental , com compilação iterativa em programas eficientes em tempo de execução. Esse desenvolvimento incremental geralmente é feito interativamente sem interromper o aplicativo em execução.

Ele também suporta anotação e conversão de tipo opcionais, que podem ser adicionadas conforme necessário nos estágios posteriores de criação de perfil e otimização, para permitir que o compilador gere código mais eficiente. Por exemplo, fixnumpode conter um inteiro unboxed em um intervalo suportado pelo hardware e implementação, permitindo aritmética mais eficiente do que em inteiros grandes ou tipos de precisão arbitrária. Da mesma forma, o compilador pode ser informado por módulo ou por função que tipo de nível de segurança é desejado, usando declarações de otimização .

Common Lisp inclui CLOS , um sistema de objetos que suporta multimétodos e combinações de métodos. Muitas vezes, é implementado com um protocolo de metaobjeto .

Common Lisp é extensível por meio de recursos padrão, como macros Lisp (transformações de código) e macros de leitor (analisadores de entrada para caracteres).

Common Lisp fornece compatibilidade retroativa parcial com Maclisp e Lisp original de John McCarthy . Isso permite que software Lisp mais antigo seja portado para Common Lisp. [6]

História

O trabalho em Common Lisp começou em 1981 após uma iniciativa do gerente da ARPA Bob Engelmore para desenvolver um único dialeto Lisp padrão da comunidade. [7] Grande parte do design inicial da linguagem foi feito via correio eletrônico. [8] [9] Em 1982, Guy L. Steele Jr. deu a primeira visão geral do Common Lisp no Simpósio ACM de 1982 sobre LISP e programação funcional. [10]

A primeira documentação de linguagem foi publicada em 1984 como Common Lisp the Language (conhecida como CLtL1), primeira edição. Uma segunda edição (conhecida como CLtL2), publicada em 1990, incorporou muitas mudanças na linguagem, feitas durante o processo de padronização ANSI Common Lisp: sintaxe LOOP estendida, o Common Lisp Object System, o Condition System para tratamento de erros, uma interface para o impressora bonita e muito mais. Mas CLtL2 não descreve o padrão ANSI Common Lisp final e, portanto, não é uma documentação do ANSI Common Lisp. O padrão ANSI Common Lisp final foi publicado em 1994. Desde então, nenhuma atualização do padrão foi publicada. Várias extensões e melhorias para Common Lisp (exemplos são Unicode, Concurrency, E/S baseada em CLOS) foram fornecidas por implementações e bibliotecas.

Sintaxe

Common Lisp é um dialeto de Lisp. Ele usa expressões S para denotar código e estrutura de dados. Chamadas de função, formulários de macro e formulários especiais são escritos como listas, com o nome do operador primeiro, como nestes exemplos:

 ( +  2  2 )            ; adiciona 2 e 2, resultando em 4. O nome da função é '+'. Lisp não tem operadores como tal.
 ( defvar  *x* )       ; Garante que existe uma variável *x*, 
                   ; sem lhe dar valor. Os asteriscos fazem parte de 
                   ; o nome, denotando por convenção uma variável especial (global). 
                   ; O símbolo *x* também é dotado da propriedade que 
                   ; as ligações subsequentes são dinâmicas, em vez de léxicas. 
 ( setf  *x*  42,1 )    ; Define a variável *x* para o valor de ponto flutuante 42.1
 ;; Defina uma função que eleva um número ao quadrado: 
 ( defun  square  ( x ) 
   ( *  x  x ))
 ;; Execute a função: 
 ( quadrado  3 )         ; Retorna 9
 ;; A construção 'let' cria um escopo para variáveis ​​locais. Aqui 
 ;; a variável 'a' está ligada a 6 e a variável 'b' está ligada 
 ;; a 4. Dentro do 'let' há um 'corpo', onde o último valor calculado é retornado. 
 ;; Aqui, o resultado da adição de a e b é retornado da expressão 'let'. 
 ;; As variáveis ​​aeb têm escopo lexical, a menos que os símbolos tenham sido 
 ;; marcadas como variáveis ​​especiais (por exemplo, por um DEFVAR anterior). 
 ( deixe  (( a  6 ) 
       ( b  4 )) 
   ( +  a  b ))         ; retorna 10

Tipos de dados

Common Lisp tem muitos tipos de dados .

Tipos escalares

Os tipos de números incluem inteiros , proporções , números de ponto flutuante e números complexos . [11] Common Lisp usa bignums para representar valores numéricos de tamanho e precisão arbitrários. O tipo de razão representa exatamente frações, uma facilidade não disponível em muitos idiomas. Common Lisp automaticamente coage valores numéricos entre esses tipos conforme apropriado.

O tipo de caractere Common Lisp não está limitado a caracteres ASCII . A maioria das implementações modernas permite caracteres Unicode . [12]

O tipo de símbolo é comum às linguagens Lisp, mas amplamente desconhecido fora delas. Um símbolo é um objeto de dados único e nomeado com várias partes: nome, valor, função, lista de propriedades e pacote. Destes, a célula de valor e a célula de função são as mais importantes. Símbolos em Lisp são frequentemente usados ​​de forma semelhante a identificadores em outras linguagens: para manter o valor de uma variável; no entanto, existem muitos outros usos. Normalmente, quando um símbolo é avaliado, seu valor é retornado. Alguns símbolos avaliam por si mesmos, por exemplo, todos os símbolos no pacote de palavras-chave são autoavaliados. Os valores booleanos em Common Lisp são representados pelos símbolos de autoavaliação T e NIL. Common Lisp tem namespaces para símbolos, chamados 'pacotes'.

Várias funções estão disponíveis para arredondar valores numéricos escalares de várias maneiras. A função roundarredonda o argumento para o inteiro mais próximo, com os casos a meio caminho arredondados para o inteiro par. As funções truncate, floore ceilingarredondam para zero, para baixo ou para cima, respectivamente. Todas essas funções retornam a parte fracionária descartada como um valor secundário. Por exemplo, (floor -2.5)produz -3, 0,5; (ceiling -2.5)produz -2, -0,5; (round 2.5)produz 2, 0,5; e (round 3.5)produz 4, -0,5.

Estruturas de dados

Os tipos de sequência em Common Lisp incluem listas, vetores, vetores de bits e strings. Existem muitas operações que podem funcionar em qualquer tipo de sequência.

Como em quase todos os outros dialetos de Lisp, as listas em Common Lisp são compostas de conses , às vezes chamadas de cons cells ou pairs . Um contra é uma estrutura de dados com dois slots, chamados car e cdr . Uma lista é uma cadeia encadeada de conses ou a lista vazia. O carro de cada contra se refere a um membro da lista (possivelmente outra lista). O cdr de cada contra se refere aos próximos contras—exceto para os últimos contras em uma lista, cujo cdr se refere ao nilvalor. Conses também podem ser facilmente usados ​​para implementar árvores e outras estruturas de dados complexas; embora geralmente seja aconselhável usar instâncias de estrutura ou classe. Também é possível criar estruturas de dados circulares com conses.

Common Lisp suporta arrays multidimensionais e pode redimensionar dinamicamente arrays ajustáveis ​​se necessário. Arrays multidimensionais podem ser usados ​​para matemática de matrizes. Um vetor é uma matriz unidimensional. Arrays podem carregar qualquer tipo como membros (mesmo tipos mistos no mesmo array) ou podem ser especializados para conter um tipo específico de membros, como em um vetor de bits. Normalmente, apenas alguns tipos são suportados. Muitas implementações podem otimizar funções de array quando o array usado é especializado em tipo. Dois tipos de arrays especializados em tipos são padrão: uma string é um vetor de caracteres, enquanto um bit-vetor é um vetor de bits .

As tabelas de hash armazenam associações entre objetos de dados. Qualquer objeto pode ser usado como chave ou valor. As tabelas de hash são redimensionadas automaticamente conforme necessário.

Pacotes são coleções de símbolos, usados ​​principalmente para separar as partes de um programa em namespaces . Um pacote pode exportar alguns símbolos, marcando-os como parte de uma interface pública. Os pacotes podem usar outros pacotes.

As estruturas , semelhantes em uso às estruturas C e registros Pascal , representam estruturas de dados complexas arbitrárias com qualquer número e tipo de campos (chamados slots ). As estruturas permitem herança única.

As classes são semelhantes às estruturas, mas oferecem recursos mais dinâmicos e herança múltipla. (Ver FECHAMENTO ). As classes foram adicionadas tardiamente ao Common Lisp e há alguma sobreposição conceitual com as estruturas. Objetos criados de classes são chamados de Instâncias . Um caso especial são as Funções Genéricas. Funções genéricas são funções e instâncias.

Funções

Common Lisp suporta funções de primeira classe . Por exemplo, é possível escrever funções que recebam outras funções como argumentos ou funções de retorno também. Isso torna possível descrever operações muito gerais.

A biblioteca Common Lisp depende muito dessas funções de ordem superior. Por exemplo, a sortfunção usa um operador relacional como um argumento e uma função de chave como um argumento de palavra-chave opcional. Isso pode ser usado não apenas para classificar qualquer tipo de dados, mas também para classificar estruturas de dados de acordo com uma chave.

 ;; Classifica a lista usando as funções > e < como o operador relacional. 
 ( sort  ( list  5  2  6  3  1  4 )  #' > )    ; Retorna (6 5 4 3 2 1) 
 ( sort  ( list  5  2  6  3  1  4 )  #' < )    ; Devoluções (1 2 3 4 5 6)
 ;; Classifica a lista de acordo com o primeiro elemento de cada sublista. 
 ( sort  ( list  ' ( 9A  ) ' ( 3B ) ' ( 4C ) ) # ' < : key #' first ) ; Devoluções ((3 B) (4 C) (9 A))          

O modelo de avaliação de funções é muito simples. Quando o avaliador encontra um formulário (f a1 a2...), ele presume que o símbolo denominado f é um dos seguintes:

  1. Um operador especial (facilmente verificado em uma lista fixa)
  2. Um operador de macro (deve ter sido definido anteriormente)
  3. O nome de uma função (padrão), que pode ser um símbolo ou um subformulário que começa com o símbolo lambda.

Se f for o nome de uma função, então os argumentos a1, a2, ..., an são avaliados na ordem da esquerda para a direita, e a função é encontrada e invocada com esses valores fornecidos como parâmetros.

Definindo funções

A macrodefun define funções onde uma definição de função fornece o nome da função, os nomes de quaisquer argumentos e um corpo de função:

 ( defun  quadrado  ( x ) 
   ( *  x  x ))

As definições de função podem incluir diretivas de compilador , conhecidas como declarações , que fornecem dicas ao compilador sobre configurações de otimização ou os tipos de dados de argumentos. Eles também podem incluir strings de documentação (docstrings), que o sistema Lisp pode usar para fornecer documentação interativa:

 ( defun  square  ( x ) 
   "Calcula o quadrado do flutuador simples x." 
   ( declare  ( float único  x )  ( otimizar  ( velocidade  3 )  ( debug  0 )  ( segurança  1 ))) 
   ( o  flutuador simples  ( *  x  x )))

Funções anônimas ( literais de função ) são definidas usando lambdaexpressões, por exemplo, (lambda (x) (* x x))para uma função que eleva seu argumento ao quadrado. O estilo de programação Lisp frequentemente usa funções de ordem superior para as quais é útil fornecer funções anônimas como argumentos.

As funções locais podem ser definidas com flete labels.

 ( flet  (( quadrado  ( x ) 
          ( *  x  x ))) 
   ( quadrado  3 ))

Existem vários outros operadores relacionados à definição e manipulação de funções. Por exemplo, uma função pode ser compilada com o compileoperador. (Alguns sistemas Lisp executam funções usando um interpretador por padrão, a menos que sejam instruídos a compilar; outros compilam todas as funções).

Definindo funções e métodos genéricos

A macro defgenericdefine funções genéricas . Funções genéricas são uma coleção de métodos . A macro defmethoddefine métodos.

Os métodos podem especializar seus parâmetros sobre classes padrão CLOS, classes de sistema, classes de estrutura ou objetos individuais. Para muitos tipos, existem classes de sistema correspondentes .

Quando uma função genérica é chamada, o despacho múltiplo determinará o método efetivo a ser usado.

 ( defgeneric  add  ( a  b ))
 ( defmethod  add  (( um  número )  ( b  número )) 
   ( +  a  b ))
 ( defmethod  add  (( a  vector )  ( b  number )) 
   ( map  'vector  ( lambda  ( n )  ( +  n  b ))  a ))
 ( defmethod  add  (( a  vector )  ( b  vector )) 
   ( map  'vector  #' +  a  b ))
( defmethod  add  (( a  string )  ( b  string )) 
  ( concatenar  'string  a  b ))
 ( adicionar  2  3 )                    ; retorna 5 
 ( adicione  #( 1  2  3  4 )  7 )           ; retorna #(8 9 10 11) 
 ( add  #( 1  2  3  4 )  #( 4  3  2  1 ))  ; retorna #(5 5 5 5) 
 ( add  "COMMON "  "LISP" )       ; retorna "LISP COMUM"

Funções genéricas também são um tipo de dados de primeira classe . Há muito mais recursos para Funções e Métodos Genéricos do que os descritos acima.

O namespace da função

O namespace para nomes de funções é separado do namespace para variáveis ​​de dados. Esta é uma diferença fundamental entre Common Lisp e Scheme . Para Common Lisp, os operadores que definem nomes no namespace da função incluem defun, flet, labelse . defmethoddefgeneric

Para passar uma função pelo nome como argumento para outra função, deve-se usar o functionoperador especial, comumente abreviado como #'. O primeiro sortexemplo acima refere-se à função nomeada pelo símbolo >no namespace da função, com o código #'>. Por outro lado, para chamar uma função passada dessa maneira, deve-se usar o funcalloperador no argumento.

O modelo de avaliação do Scheme é mais simples: há apenas um namespace e todas as posições no formulário são avaliadas (em qualquer ordem) – não apenas os argumentos. O código escrito em um dialeto é, portanto, às vezes confuso para programadores mais experientes no outro. Por exemplo, muitos programadores de Common Lisp gostam de usar nomes de variáveis ​​descritivos, como lista ou string , o que pode causar problemas no Scheme, pois ocultaria localmente os nomes das funções.

Se um namespace separado para funções é uma vantagem é uma fonte de contenção na comunidade Lisp. É normalmente referido como o debate Lisp-1 vs. Lisp-2 . Lisp-1 refere-se ao modelo de Scheme e Lisp-2 refere-se ao modelo de Common Lisp. Esses nomes foram cunhados em um artigo de 1988 de Richard P. Gabriel e Kent Pitman , que compara extensivamente as duas abordagens. [13]

Vários valores de retorno

Common Lisp suporta o conceito de múltiplos valores , [14] onde qualquer expressão sempre tem um único valor primário , mas também pode ter qualquer número de valores secundários , que podem ser recebidos e inspecionados por chamadores interessados. Esse conceito é diferente de retornar um valor de lista, pois os valores secundários são totalmente opcionais e transmitidos por meio de um canal lateral dedicado. Isso significa que os chamadores podem permanecer totalmente inconscientes dos valores secundários que estão lá se não precisarem deles, e torna conveniente usar o mecanismo para comunicar informações que às vezes são úteis, mas nem sempre necessárias. Por exemplo,

  • A TRUNCATEfunção [15] arredonda o número dado para um inteiro em direção a zero. No entanto, ele também retorna um resto como valor secundário, tornando muito fácil determinar qual valor foi truncado. Ele também suporta um parâmetro divisor opcional, que pode ser usado para realizar a divisão euclidiana trivialmente:
( let  (( x  1266778 ) 
      ( y  458 )) 
  ( multiple-value-bind  ( quociente  restante ) 
      ( truncar  x  y ) 
    ( formato  nil  "~A dividido por ~A é ~A restante ~A"  x  y  quociente  restante )) )

;;;; => "1266778 dividido por 458 é 2765 restante 408"
  • GETHASH[16] retorna o valor de uma chave em um mapa associativo , ou o valor padrão caso contrário, e um booleano secundário indicando se o valor foi encontrado. Assim, o código que não se importa se o valor foi encontrado ou fornecido como padrão pode simplesmente usá-lo como está, mas quando essa distinção é importante, ele pode inspecionar o booleano secundário e reagir adequadamente. Ambos os casos de uso são suportados pela mesma chamada e nenhum é sobrecarregado ou restringido desnecessariamente pelo outro. Ter esse recurso no nível da linguagem elimina a necessidade de verificar a existência da chave ou compará-la com null como seria feito em outras linguagens.
( defun  get-answer  ( library ) 
  ( gethash  'answer  library  42 ))

( defun  the-answer-1  ( library ) 
  ( format  nil  "A resposta é ~A"  ( get-answer  library ))) 
;;;; Retorna "A resposta é 42" se ANSWER não estiver presente na LIBRARY

( defun  the-answer-2  ( library ) 
  ( multiple-value-bind  ( answer  sure-p ) 
      ( get-answer  library ) 
    ( if  ( não  tenho certeza-p ) 
        "Não sei" 
     ( format  nil  "A resposta é ~A"  resposta )))) 
;;;; Retorna "I don't know" se ANSWER não estiver presente na LIBRARY

Vários valores são suportados por um punhado de formulários padrão, sendo os mais comuns o MULTIPLE-VALUE-BINDformulário especial para acessar valores secundários e VALUESretornar vários valores:

( defun  magic-eight-ball  () 
  "Retorna uma previsão do Outlook, com a probabilidade como valor secundário" 
  ( valores  "Outlook good"  ( random  1.0 )))

;;;; => "Boa perspectiva" 
;;;; => 0,3187

Outros tipos

Outros tipos de dados em Common Lisp incluem:

  • Os nomes de caminho representam arquivos e diretórios no sistema de arquivos . O recurso de nome de caminho do Common Lisp é mais geral do que as convenções de nomenclatura de arquivos da maioria dos sistemas operacionais, tornando o acesso dos programas Lisp a arquivos amplamente portáveis ​​em diversos sistemas.
  • Os fluxos de entrada e saída representam fontes e coletores de dados binários ou textuais, como o terminal ou arquivos abertos.
  • Common Lisp tem um gerador de números pseudo-aleatórios (PRNG) embutido. Objetos de estado aleatório representam fontes reutilizáveis ​​de números pseudo-aleatórios, permitindo que o usuário semeie o PRNG ou faça com que ele reproduza uma sequência.
  • Condições são um tipo usado para representar erros, exceções e outros eventos "interessantes" aos quais um programa pode responder.
  • Classes são objetos de primeira classe , e são elas próprias instâncias de classes chamadas classes metaobjetos ( metaclasses para abreviar).
  • Readtables são um tipo de objeto que controla como o leitor do Common Lisp analisa o texto do código-fonte. Ao controlar qual tabela de leitura está em uso quando o código é lido, o programador pode alterar ou estender a sintaxe da linguagem.

Escopo

Como programas em muitas outras linguagens de programação, os programas Common Lisp usam nomes para se referir a variáveis, funções e muitos outros tipos de entidades. As referências nomeadas estão sujeitas ao escopo.

A associação entre um nome e a entidade à qual o nome se refere é chamada de vinculação.

Escopo refere-se ao conjunto de circunstâncias em que um nome é determinado como tendo uma ligação específica.

Determinantes do escopo

As circunstâncias que determinam o escopo no Common Lisp incluem:

  • a localização de uma referência dentro de uma expressão. Se for a posição mais à esquerda de um composto, refere-se a um operador especial ou a uma macro ou ligação de função, caso contrário, a uma ligação de variável ou outra coisa.
  • o tipo de expressão em que a referência ocorre. Por exemplo, (go x)significa transferir o controle para label x, enquanto (print x)se refere à variável x. Ambos os escopos de xpodem estar ativos na mesma região do texto do programa, uma vez que os rótulos de tagbody estão em um namespace separado dos nomes de variáveis. Um formulário especial ou formulário macro tem controle total sobre os significados de todos os símbolos em sua sintaxe. Por exemplo, em (defclass x (a b) ()), uma definição de classe, o (a b)é uma lista de classes base, então esses nomes são procurados no espaço de nomes de classe e xnão é uma referência a uma ligação existente, mas o nome de uma nova classe sendo derivada de ae b. Esses fatos emergem puramente da semântica dedefclass. O único fato genérico sobre essa expressão é que defclassse refere a uma macro vinculação; tudo o resto depende defclass.
  • a localização da referência no texto do programa. Por exemplo, se uma referência a variável xfor incluída em uma construção de associação como a letque define uma associação para x, a referência estará no escopo criado por essa associação.
  • para uma referência de variável, se um símbolo de variável foi ou não, local ou globalmente, declarado especial. Isso determina se a referência é resolvida em um ambiente léxico ou em um ambiente dinâmico.
  • a instância específica do ambiente em que a referência é resolvida. Um ambiente é um dicionário de tempo de execução que mapeia símbolos para associações. Cada tipo de referência usa seu próprio tipo de ambiente. Referências a variáveis ​​lexicais são resolvidas em um ambiente lexical, etc. Mais de um ambiente pode ser associado à mesma referência. Por exemplo, graças à recursão ou ao uso de vários threads, várias ativações da mesma função podem existir ao mesmo tempo. Essas ativações compartilham o mesmo texto do programa, mas cada uma tem sua própria instância de ambiente léxico.

Para entender a que um símbolo se refere, o programador de Common Lisp deve saber que tipo de referência está sendo expressa, que tipo de escopo ele usa se for uma referência de variável (escopo dinâmico versus lexical), e também a situação de tempo de execução: em qual ambiente é a referência resolvida, onde foi a ligação introduzida no ambiente, etc.

Tipos de ambiente

Global

Alguns ambientes em Lisp são globalmente difundidos. Por exemplo, se um novo tipo for definido, ele será conhecido em todos os lugares a partir de então. As referências a esse tipo procuram-no neste ambiente global.

Dinâmico

Um tipo de ambiente em Common Lisp é o ambiente dinâmico. As vinculações estabelecidas nesse ambiente têm extensão dinâmica, o que significa que uma vinculação é estabelecida no início da execução de algum constructo, como um letbloco, e desaparece quando esse constructo termina de ser executado: seu tempo de vida está vinculado à ativação e desativação dinâmica de um bloco. No entanto, uma associação dinâmica não é apenas visível nesse bloco; também é visível para todas as funções invocadas desse bloco. Esse tipo de visibilidade é conhecido como escopo indefinido. Ligações que exibem extensão dinâmica (tempo de vida vinculado à ativação e desativação de um bloco) e escopo indefinido (visível para todas as funções que são chamadas a partir desse bloco) são chamadas de escopo dinâmico.

Common Lisp tem suporte para variáveis ​​de escopo dinâmico, que também são chamadas de variáveis ​​especiais. Certos outros tipos de associações são necessariamente com escopo dinâmico também, como reinicializações e tags de captura. As associações de função não podem ter escopo dinâmico usando flet(que fornece apenas associações de função com escopo léxico), mas objetos de função (um objeto de primeiro nível em Common Lisp) podem ser atribuídos a variáveis ​​com escopo dinâmico, vinculados usando letescopo dinâmico e, em seguida, chamados usando funcallou APPLY.

O escopo dinâmico é extremamente útil porque adiciona clareza referencial e disciplina às variáveis ​​globais . Variáveis ​​globais são desaprovadas na ciência da computação como fontes potenciais de erro, porque podem dar origem a canais de comunicação ad-hoc e secretos entre módulos que levam a interações indesejadas e surpreendentes.

Em Common Lisp, uma variável especial que possui apenas uma ligação de nível superior se comporta exatamente como uma variável global em outras linguagens de programação. Um novo valor pode ser armazenado nele e esse valor simplesmente substitui o que está na associação de nível superior. A substituição descuidada do valor de uma variável global está no centro dos bugs causados ​​pelo uso de variáveis ​​globais. No entanto, outra maneira de trabalhar com uma variável especial é dar a ela uma nova ligação local dentro de uma expressão. Isso às vezes é chamado de "religação" da variável. A vinculação de uma variável com escopo dinâmico cria temporariamente um novo local de memória para essa variável e associa o nome a esse local. Enquanto essa associação estiver em vigor, todas as referências a essa variável referem-se à nova associação; a ligação anterior está oculta. Quando a execução da expressão de ligação termina, a localização da memória temporária desaparece e a ligação antiga é revelada, com o valor original intacto. Obviamente, várias associações dinâmicas para a mesma variável podem ser aninhadas.

Em implementações de Common Lisp que suportam multithreading, escopos dinâmicos são específicos para cada thread de execução. Assim, variáveis ​​especiais servem como uma abstração para armazenamento local de thread. Se um encadeamento revincular uma variável especial, essa religação não terá efeito sobre essa variável em outros encadeamentos. O valor armazenado em uma associação só pode ser recuperado pelo encadeamento que criou essa associação. Se cada thread vincula alguma variável especial *x*, então *x*se comporta como armazenamento local de thread. Entre os encadeamentos que não religam *x*, ele se comporta como um global comum: todos esses encadeamentos se referem à mesma ligação de nível superior de *x*.

Variáveis ​​dinâmicas podem ser usadas para estender o contexto de execução com informações de contexto adicionais que são passadas implicitamente de função para função sem ter que aparecer como um parâmetro de função extra. Isso é especialmente útil quando a transferência de controle precisa passar por camadas de código não relacionado, que simplesmente não podem ser estendidas com parâmetros extras para passar os dados adicionais. Uma situação como essa geralmente exige uma variável global. Essa variável global deve ser salva e restaurada, para que o esquema não quebre em recursão: a religação de variável dinâmica cuida disso. E essa variável deve ser thread-local (ou então um mutex grande deve ser usado) para que o esquema não quebre em threads: implementações de escopo dinâmico também podem cuidar disso.

Na biblioteca Common Lisp, existem muitas variáveis ​​especiais padrão. Por exemplo, todos os fluxos de E/S padrão são armazenados nas ligações de nível superior de variáveis ​​especiais conhecidas. O fluxo de saída padrão é armazenado em *saída padrão*.

Suponha que uma função foo grave na saída padrão:

  ( defun  foo  () 
    ( formato  t  "Olá, mundo" ))

Para capturar sua saída em uma string de caracteres, *standard-output* pode ser vinculado a um fluxo de string e chamado:

  ( sem saída para string  ( * saída padrão * ) 
    ( foo ))
-> "Olá, mundo"; saída coletada retornada como uma string

Lexical

Common Lisp suporta ambientes léxicos. Formalmente, as ligações em um ambiente léxico têm escopo léxico e podem ter extensão indefinida ou extensão dinâmica, dependendo do tipo de namespace. O escopo léxico significa que a visibilidade é fisicamente restrita ao bloco no qual a vinculação é estabelecida. As referências que não estão textualmente (ou seja, lexicalmente) inseridas nesse bloco simplesmente não veem essa ligação.

As tags em um TAGBODY têm escopo lexical. A expressão (GO X) é errônea se não estiver embutida em um TAGBODY que contenha um rótulo X. No entanto, as vinculações de rótulo desaparecem quando o TAGBODY encerra sua execução, pois possuem extensão dinâmica. Se esse bloco de código for reinserido pela invocação de um encerramento léxico , é inválido para o corpo desse encerramento tentar transferir o controle para uma tag via GO:

  ( defvar  *escondido* )  ;; terá uma função

  ( tagbody 
    ( setf  *stashed*  ( lambda  ()  ( go  some-label ))) 
    ( go  end-label )  ;; pule o (print "Hello") 
   some-label 
    ( print  "Hello" ) 
   end-label ) 
  ->  NADA

Quando o TAGBODY é executado, ele primeiro avalia o formulário setf que armazena uma função na variável especial *stashed*. Em seguida, o (go end-label) transfere o controle para o end-label, pulando o código (print "Hello"). Como o rótulo final está no final do corpo da tag, o corpo da tag termina, resultando em NIL. Suponha que a função lembrada anteriormente seja agora chamada:

  ( funcall  *stashed* )  ;; Erro!

Esta situação é errônea. A resposta de uma implementação é uma condição de erro contendo a mensagem "GO: tagbody for tag SOME-LABEL já foi deixado". A função tentou avaliar (go some-label), que é lexicalmente incorporado no tagbody e resolve para o rótulo. No entanto, o tagbody não está sendo executado (sua extensão foi encerrada) e, portanto, a transferência de controle não pode ocorrer.

As associações de função local em Lisp têm escopo léxico e as associações de variável também têm escopo léxico por padrão. Em contraste com os rótulos GO, ambos têm extensão indefinida. Quando uma função lexical ou uma ligação de variável é estabelecida, essa ligação continua a existir enquanto as referências a ela forem possíveis, mesmo após a construção que estabeleceu essa ligação ter terminado. Referências a variáveis ​​lexicais e funções após o término de sua construção de estabelecimento são possíveis graças aos fechamentos lexicais .

A associação léxica é o modo de associação padrão para variáveis ​​Common Lisp. Para um símbolo individual, ele pode ser alternado para escopo dinâmico, seja por uma declaração local, seja por uma declaração global. O último pode ocorrer implicitamente através do uso de uma construção como DEFVAR ou DEFPARAMETER. É uma convenção importante na programação em Common Lisp que variáveis ​​especiais (ou seja, com escopo dinâmico) tenham nomes que começam e terminam com um sigilo * de asterisco no que é chamado de " convenção de abafador de ouvido". [17] Se respeitada, esta convenção cria efetivamente um espaço de nomes separado para variáveis ​​especiais, de modo que variáveis ​​destinadas a serem léxicas não se tornem especiais acidentalmente.

O escopo léxico é útil por vários motivos.

Em primeiro lugar, as referências a variáveis ​​e funções podem ser compiladas em código de máquina eficiente, porque a estrutura do ambiente de tempo de execução é relativamente simples. Em muitos casos, ele pode ser otimizado para armazenamento em pilha, portanto, abrir e fechar escopos léxicos tem sobrecarga mínima. Mesmo nos casos em que fechamentos completos devem ser gerados, o acesso ao ambiente do fechamento ainda é eficiente; normalmente, cada variável se torna um deslocamento em um vetor de associações e, portanto, uma referência de variável se torna uma instrução simples de carregamento ou armazenamento com um modo de endereçamento de base mais deslocamento .

Em segundo lugar, o escopo lexical (combinado com extensão indefinida) dá origem ao fechamento lexical , que por sua vez cria todo um paradigma de programação centrado no uso de funções como objetos de primeira classe, que está na raiz da programação funcional.

Em terceiro lugar, talvez o mais importante, mesmo que os fechamentos lexicais não sejam explorados, o uso do escopo lexical isola os módulos do programa de interações indesejadas. Devido à sua visibilidade restrita, as variáveis ​​lexicais são privadas. Se um módulo A vincular uma variável lexical X e chamar outro módulo B, as referências a X em B não resolverão acidentalmente o limite X em A. B simplesmente não tem acesso a X. Para situações em que interações disciplinadas por meio de uma variável são desejável, o Common Lisp fornece variáveis ​​especiais. Variáveis ​​especiais permitem que um módulo A configure uma ligação para uma variável X que é visível para outro módulo B, chamado de A. Ser capaz de fazer isso é uma vantagem, e ser capaz de evitar que isso aconteça também é uma vantagem; consequentemente, Common Lisp suporta escopo lexical e dinâmico .

Macros

Uma macro em Lisp se assemelha superficialmente a uma função em uso. No entanto, em vez de representar uma expressão que é avaliada, ela representa uma transformação do código-fonte do programa. A macro obtém a fonte que ela envolve como argumentos, liga-os aos seus parâmetros e calcula um novo formulário de fonte. Este novo formulário também pode usar uma macro. A expansão da macro é repetida até que o novo formulário de origem não use uma macro. A forma computada final é o código-fonte executado em tempo de execução.

Usos típicos de macros em Lisp:

  • novas estruturas de controle (exemplo: construções de loop, construções de ramificação)
  • construtos de escopo e ligação
  • sintaxe simplificada para código-fonte complexo e repetido
  • formulários de definição de nível superior com efeitos colaterais em tempo de compilação
  • programação orientada a dados
  • linguagens específicas de domínio incorporadas (exemplos: SQL , HTML , Prolog )
  • formulários de finalização implícitos

Vários recursos padrão do Common Lisp também precisam ser implementados como macros, como:

  • a abstração padrão setf, para permitir expansões personalizadas em tempo de compilação de operadores de atribuição/acesso
  • with-accessors, with-slotse with-open-fileoutras WITHmacros semelhantes
  • Dependendo da implementação, ifou condseja uma macro construída sobre a outra, o operador especial; whene unlessconsiste em macros
  • A poderosa looplinguagem específica de domínio

As macros são definidas pela macro defmacro . O operador especial macrolet permite a definição de macros locais (com escopo lexical). Também é possível definir macros para símbolos usando define-symbol-macro e symbol-macrolet .

O livro de Paul Graham On Lisp descreve o uso de macros em Common Lisp em detalhes. O livro de Doug Hoyte , Let Over Lambda , estende a discussão sobre macros, afirmando que "Macros são a maior vantagem que o lisp tem como linguagem de programação e a maior vantagem de qualquer linguagem de programação". Hoyte fornece vários exemplos de desenvolvimento iterativo de macros.

Exemplo usando uma macro para definir uma nova estrutura de controle

As macros permitem que os programadores de Lisp criem novas formas sintáticas na linguagem. Um uso típico é criar novas estruturas de controle. A macro de exemplo fornece uma untilconstrução de loop. A sintaxe é:

(até formulário de teste*)

A definição de macro para até :

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

tagbody é um operador especial primitivo do Common Lisp que fornece a capacidade de nomear tags e usar o formulário go para pular para essas tags. O backquote ` fornece uma notação que fornece modelos de código, onde o valor dos formulários precedidos por uma vírgula é preenchido. Formulários precedidos por vírgula e arroba são emendados . O formulário tagbody testa a condição final. Se a condição for verdadeira, ela salta para a tag final. Caso contrário, o código do corpo fornecido é executado e, em seguida, salta para a tag inicial.

Um exemplo de uso da macro até acima:

( até  ( =  ( aleatório  10 )  0 ) 
  ( linha de escrita  "Olá" ))

O código pode ser expandido usando a função macroexpand-1 . A expansão para o exemplo acima é assim:

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

Durante a expansão da macro, o valor da variável test é (= (random 10) 0) e o valor do corpo da variável é ((write-line "Hello")) . O corpo é uma lista de formulários.

Os símbolos geralmente são convertidos em maiúsculas automaticamente. A expansão utiliza o TAGBODY com dois rótulos. Os símbolos para esses rótulos são calculados pela GENSYM e não estão contidos em nenhum pacote. Dois formulários go usam essas tags para pular. Como tagbody é um operador primitivo em Common Lisp (e não uma macro), ele não será expandido para outra coisa. O formulário expandido usa a macro when , que também será expandida. A expansão total de um formulário de origem é chamada de caminhada de código .

No formulário totalmente expandido ( walked ), o formulário when é substituído pelo primitivo if :

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

Todas as macros devem ser expandidas antes que o código-fonte que as contém possa ser avaliado ou compilado normalmente. As macros podem ser consideradas funções que aceitam e retornam expressões S – semelhantes às árvores de sintaxe abstratas , mas não limitadas a elas. Essas funções são invocadas antes do avaliador ou compilador para produzir o código-fonte final. As macros são escritas em Common Lisp normal e podem usar qualquer operador de Common Lisp (ou de terceiros) disponível.

Captura variável e sombreamento

As macros Common Lisp são capazes do que é comumente chamado de captura de variáveis , onde os símbolos no corpo da macro-expansão coincidem com aqueles no contexto de chamada, permitindo ao programador criar macros em que vários símbolos têm significado especial. O termo captura de variável é um pouco enganoso, porque todos os namespaces são vulneráveis ​​à captura indesejada, incluindo o namespace do operador e da função, o namespace do rótulo tagbody, a tag catch, o manipulador de condição e os namespaces de reinicialização.

A captura de variável pode introduzir defeitos de software. Isso acontece de uma das duas maneiras a seguir:

  • Na primeira maneira, uma expansão de macro pode inadvertidamente fazer uma referência simbólica que o gravador de macro assumiu que resolveria em um namespace global, mas o código em que a macro é expandida fornece uma definição de sombra local que rouba essa referência. Deixe isso ser referido como captura tipo 1.
  • A segunda maneira, a captura do tipo 2, é exatamente o oposto: alguns dos argumentos da macro são pedaços de código fornecidos pelo chamador da macro, e esses pedaços de código são escritos de tal forma que fazem referências às associações circundantes. No entanto, a macro insere esses pedaços de código em uma expansão que define suas próprias ligações que capturam acidentalmente algumas dessas referências.

O dialeto Scheme do Lisp fornece um sistema de escrita de macros que fornece a transparência referencial que elimina ambos os tipos de problemas de captura. Este tipo de macrossistema é por vezes denominado "higiénico", em particular pelos seus proponentes (que consideram não higiénicos os macrossistemas que não resolvem automaticamente este problema). [ citação necessária ]

No Common Lisp, a higiene macro é assegurada de duas maneiras diferentes.

Uma abordagem é usar gensyms : símbolos únicos garantidos que podem ser usados ​​em uma macroexpansão sem ameaça de captura. O uso de gensyms em uma definição de macro é uma tarefa manual, mas macros podem ser escritas para simplificar a instanciação e uso de gensyms. Gensyms resolve a captura tipo 2 facilmente, mas eles não são aplicáveis ​​à captura tipo 1 da mesma forma, porque a expansão da macro não pode renomear os símbolos interferentes no código ao redor que capturam suas referências. Gensyms pode ser usado para fornecer aliases estáveis ​​para os símbolos globais que a expansão macro precisa. A expansão da macro usaria esses aliases secretos em vez dos nomes conhecidos, portanto, a redefinição dos nomes conhecidos não teria nenhum efeito negativo na macro.

Outra abordagem é usar pacotes. Uma macro definida em seu próprio pacote pode simplesmente usar símbolos internos desse pacote em sua expansão. O uso de pacotes trata da captura tipo 1 e tipo 2.

No entanto, os pacotes não resolvem a captura tipo 1 de referências a funções e operadores padrão do Common Lisp. A razão é que o uso de pacotes para resolver problemas de captura gira em torno do uso de símbolos privados (símbolos em um pacote, que não são importados ou tornados visíveis em outros pacotes). Enquanto os símbolos da biblioteca Common Lisp são externos e frequentemente importados ou tornados visíveis em pacotes definidos pelo usuário.

Segue um exemplo de captura indesejada no namespace do operador, ocorrendo na expansão de uma macro:

 ;; expansão de UNTIL faz uso liberal de DO 
 ( defmacro  until  ( expressão  & corpo  corpo ) 
   ` ( do  ( )  ( , expressão )  ,@ corpo ))

 ;; macrolet estabelece ligação de operador léxico para DO 
 ( macrolet  (( do  ( ... )  ...  outra coisa  ...  ) ) 
   ( until  ( =  ( random  10 )  0 )  ( write-line  "Hello" )))

A untilmacro se expandirá em um formulário que chama doque se destina a se referir à macro padrão do Common Lisp do. No entanto, neste contexto, dopode ter um significado completamente diferente, portanto, untilpode não funcionar corretamente.

Common Lisp resolve o problema do sombreamento de operadores e funções padrão, proibindo sua redefinição. Como ele redefine o operador padrão do, o anterior é na verdade um fragmento de Common Lisp não conforme, que permite que as implementações o diagnostiquem e o rejeitem.

Sistema de condições

O sistema de condições é responsável pelo tratamento de exceções em Common Lisp. [18] Fornece condições , manipuladores e reinícios . Condição s são objetos que descrevem uma situação excepcional (por exemplo, um erro). Se uma condição for sinalizada, o sistema Common Lisp procura um manipulador para esse tipo de condição e chama o manipulador. O manipuladoragora pode procurar por reinicializações e usar uma dessas reinicializações para reparar automaticamente o problema atual, usando informações como o tipo de condição e qualquer informação relevante fornecida como parte do objeto de condição e chamar a função de reinicialização apropriada.

Essas reinicializações, se não tratadas por código, podem ser apresentadas aos usuários (como parte de uma interface de usuário, a de um depurador, por exemplo), para que o usuário possa selecionar e invocar uma das reinicializações disponíveis. Como o manipulador de condição é chamado no contexto do erro (sem desenrolar a pilha), a recuperação completa do erro é possível em muitos casos, onde outros sistemas de tratamento de exceção já teriam encerrado a rotina atual. O próprio depurador também pode ser personalizado ou substituído usando a *debugger-hook*variável dinâmica. O código encontrado em formulários de proteção contra desenrolamento , como finalizadores, também será executado conforme apropriado, apesar da exceção.

No exemplo a seguir (usando Symbolics Genera ), o usuário tenta abrir um arquivo em um teste de função Lisp chamado do Read-Eval-Print-LOOP ( REPL ), quando o arquivo não existe. O sistema Lisp apresenta quatro reinícios. O usuário seleciona Retry OPEN usando uma reinicialização de nome de caminho diferente e insere um nome de caminho diferente (lispm-init.lisp em vez de lispm-int.lisp). O código do usuário não contém nenhum código de tratamento de erros. Todo o código de manipulação e reinicialização de erros é fornecido pelo sistema Lisp, que pode manipular e reparar o erro sem encerrar o código do usuário.

Comando: (teste ">zippy>lispm-int.lisp")

Erro: O arquivo não foi encontrado.
       Para lispm:>zippy>lispm-int.lisp.newest

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

sA, <Resume>: Tente novamente OPEN de lispm:>zippy>lispm-int.lisp.newest
sB: Tente novamente OPEN usando um nome de caminho diferente
sC, <Abort>: Retornar ao Lisp Top Level em um servidor TELNET
sD: Reinicie o processo de terminal TELNET

-> Tente novamente OPEN usando um nome de caminho diferente
Use o nome do caminho [default lispm:>zippy>lispm-int.lisp.newest]:
   lispm:>zippy>lispm-init.lisp.newest

...o programa continua

Common Lisp Object System (CLOS)

Common Lisp inclui um kit de ferramentas para programação orientada a objetos , o Common Lisp Object System ou CLOS , que é um dos sistemas de objetos mais poderosos disponíveis em qualquer linguagem. Por exemplo, Peter Norvig explica quantos Design Patterns são mais simples de implementar em uma linguagem dinâmica com os recursos de CLOS (Multiple Inheritance, Mixins, Multimethods, Metaclasses, Methods combinados, etc.). [19] Várias extensões para Common Lisp para programação orientada a objetos foram propostas para serem incluídas no padrão ANSI Common Lisp, mas eventualmente o CLOS foi adotado como o sistema de objetos padrão para Common Lisp. CLOS é um sistema de objetos dinâmicos com múltiplos despachose herança múltipla , e difere radicalmente das facilidades OOP encontradas em linguagens estáticas como C++ ou Java . Como um sistema de objetos dinâmicos, o CLOS permite mudanças em tempo de execução para funções e classes genéricas. Métodos podem ser adicionados e removidos, classes podem ser adicionadas e redefinidas, objetos podem ser atualizados para mudanças de classe e a classe de objetos pode ser alterada.

O CLOS foi integrado ao ANSI Common Lisp. Funções genéricas podem ser usadas como funções normais e são um tipo de dados de primeira classe. Cada classe CLOS é integrada ao sistema do tipo Common Lisp. Muitos tipos de Common Lisp têm uma classe correspondente. Há mais uso potencial de CLOS para Common Lisp. A especificação não diz se as condições são implementadas com CLOS. Nomes de caminho e fluxos podem ser implementados com CLOS. Essas outras possibilidades de uso do CLOS para ANSI Common Lisp não fazem parte do padrão. As implementações reais do Common Lisp usam CLOS para nomes de caminho, fluxos, entrada-saída, condições, a implementação do próprio CLOS e muito mais.

Compilador e interpretador

Um interpretador Lisp executa diretamente o código fonte Lisp fornecido como objetos Lisp (listas, símbolos, números, ...) lidos de expressões s. Um compilador Lisp gera bytecode ou código de máquina a partir do código-fonte Lisp. Common Lisp permite que ambas as funções Lisp individuais sejam compiladas na memória e a compilação de arquivos inteiros para código compilado armazenado externamente ( arquivos fasl ).

Várias implementações de dialetos Lisp anteriores forneciam um interpretador e um compilador. Infelizmente, muitas vezes a semântica era diferente. Esses Lisps anteriores implementaram o escopo léxico no compilador e o escopo dinâmico no interpretador. Common Lisp requer que o interpretador e o compilador usem escopo léxico por padrão. O padrão Common Lisp descreve a semântica do interpretador e do compilador. O compilador pode ser chamado usando a função compile para funções individuais e usando a função compile-file para arquivos. Common Lisp permite declarações de tipo e fornece maneiras de influenciar a política de geração de código do compilador. Para este último, várias qualidades de otimização podem receber valores entre 0 (não importante) e 3 (mais importante): velocidade, espaço , segurança , depuração e velocidade de compilação .

Há também uma função para avaliar o código Lisp: eval. evalrecebe o código como expressões s pré-analisadas e não, como em algumas outras linguagens, como strings de texto. Desta forma o código pode ser construído com as funções usuais do Lisp para construção de listas e símbolos e então este código pode ser avaliado com a função eval. Várias implementações de Common Lisp (como Clozure CL e SBCL) estão sendo implementadas evalusando seu compilador. Desta forma o código é compilado, mesmo que seja avaliado usando a função eval.

O compilador de arquivos é invocado usando a função compile-file . O arquivo gerado com o código compilado é chamado de arquivo fasl (de fast load ). Esses arquivos fasl e também os arquivos de código-fonte podem ser carregados com o carregamento da função em um sistema Common Lisp em execução. Dependendo da implementação, o compilador de arquivos gera código de byte (por exemplo, para Java Virtual Machine ), código de linguagem C (que é compilado com um compilador C) ou, diretamente, código nativo.

Implementações de Common Lisp podem ser usadas interativamente, mesmo que o código seja totalmente compilado. A ideia de uma linguagem interpretada, portanto, não se aplica ao Common Lisp interativo.

A linguagem faz uma distinção entre tempo de leitura, tempo de compilação, tempo de carregamento e tempo de execução, e permite que o código do usuário também faça essa distinção para executar o tipo de processamento desejado na etapa desejada.

Alguns operadores especiais são fornecidos especialmente para o desenvolvimento interativo; por exemplo, defvarsó atribuirá um valor à variável fornecida se ela ainda não estiver vinculada, enquanto defparametersempre executará a atribuição. Essa distinção é útil ao avaliar, compilar e carregar de forma interativa o código em uma imagem ao vivo.

Alguns recursos também são fornecidos para ajudar a escrever compiladores e interpretadores. Os símbolos consistem em objetos de primeiro nível e são manipuláveis ​​diretamente pelo código do usuário. O progvoperador especial permite criar ligações léxicas programaticamente, enquanto os pacotes também são manipuláveis. O compilador Lisp está disponível em tempo de execução para compilar arquivos ou funções individuais. Isso facilita o uso do Lisp como um compilador ou interpretador intermediário para outra linguagem.

Exemplos de código

Paradoxo do aniversário

O programa a seguir calcula o menor número de pessoas em uma sala para quem a probabilidade de aniversários únicos é menor que 50% (o paradoxo do aniversário , onde para 1 pessoa a probabilidade é obviamente 100%, para 2 é 364/365, etc. ). A resposta é 23.

Por convenção, as constantes em Common Lisp são delimitadas por caracteres +.

( defconstante  + tamanho do ano +  365 )

( defun  paradoxo do aniversário  ( probabilidade  número de pessoas ) 
  ( let  (( nova probabilidade  ( *  ( /  ( -  + tamanho do ano +  número de pessoas ) 
                               + tamanho do ano + ) 
                            probabilidade ))) 
    ( if  ( <  novo -probabilidade  0,5 ) 
        ( 1+  número-de-pessoas ) 
        ( aniversário-paradoxo  nova-probabilidade  ( 1+  número-de-pessoas ))))))

Chamando a função de exemplo usando o REPL (Read Eval Print Loop):

CL-USER > (paradoxo-aniversário 1.0 1)
23

Classificando uma lista de objetos pessoa

Definimos uma classe persone um método para exibir o nome e a idade de uma pessoa. Em seguida, definimos um grupo de pessoas como uma lista de personobjetos. Em seguida, iteramos sobre a lista ordenada.

( defclass  person  () 
  (( name  :initarg  :name  : accessor  person-name ) 
   ( age   :initarg  :age   : accessor  person-age )) 
  ( :documentation  "A classe PERSON com slots NAME e AGE." ))

( defmethod  display  (( object  person )  stream ) 
  "Exibindo um objeto PERSON para um stream de saída." 
  ( 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 )) 
  "Uma lista de objetos PERSON." )

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

Imprime os três nomes com idade decrescente.

Bob (33)
Cinza (23)
Cris (16)

Exponenciando ao quadrado

O uso da macro LOOP é demonstrado:

( defun  power  ( x  n ) 
  ( loop  com  resultado  =  1 
        while  ( plusp  n ) 
        quando  ( oddp  n )  do  ( setf  resultado  ( *  resultado  x )) 
        do  ( setf  x  ( *  x  x ) 
                 n  ( truncar  n  2 )) 
        finalmente  ( resultado de retorno  )))

Exemplo de uso:

CL-USER > (potência 2 200)
1606938044258990275541962092341162602522202993782792835301376

Compare com a exponenciação incorporada:

CL-USER > (= (expt 2 200) (potência 2 200))
T

Encontre a lista de shells disponíveis

WITH-OPEN-FILE é uma macro que abre um arquivo e fornece um fluxo. Quando o formulário está retornando, o arquivo é fechado automaticamente. FUNCALL chama um objeto de função. O LOOP coleta todas as linhas que correspondem ao predicado.

( defun  list-matching-lines  ( file  predicate ) 
  "Retorna uma lista de linhas no arquivo, para as quais o predicado aplicado à 
linha retorna T." 
  ( with-open-file  ( stream  file ) 
    ( loop  for  line  =  ( read-  fluxo  de linha nil  nil ) 
          enquanto  linha 
          quando  ( linha de predicado funcall  ) coletá -lo ))) 
           

A função AVAILABLE-SHELLS chama a função acima LIST-MATCHING-LINES com um nome de caminho e uma função anônima como predicado. O predicado retorna o nome do caminho de um shell ou NIL (se a string não for o nome do arquivo de um shell).

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

Resultados de exemplo (no Mac OS X 10.6):

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

Comparação com outros Lisps

Common Lisp é mais frequentemente comparado e contrastado com Scheme — mesmo porque são os dois dialetos Lisp mais populares. Scheme é anterior ao CL, e vem não apenas da mesma tradição Lisp, mas de alguns dos mesmos engenheiros – Guy L. Steele , com quem Gerald Jay Sussman projetou Scheme, presidiu o comitê de padrões para Common Lisp.

Common Lisp é uma linguagem de programação de uso geral, em contraste com variantes de Lisp, como Emacs Lisp e AutoLISP , que são linguagens de extensão incorporadas em produtos específicos (GNU Emacs e AutoCAD, respectivamente). Ao contrário de muitos Lisps anteriores, Common Lisp (como Scheme ) usa escopo de variável léxica por padrão para código interpretado e compilado.

A maioria dos sistemas Lisp cujos projetos contribuíram para o Common Lisp — como ZetaLisp e Franz Lisp — usavam variáveis ​​com escopo dinâmico em seus interpretadores e variáveis ​​com escopo lexical em seus compiladores. Scheme introduziu o uso exclusivo de variáveis ​​com escopo léxico para Lisp; uma inspiração do ALGOL 68 . O CL também suporta variáveis ​​com escopo dinâmico, mas elas devem ser declaradas explicitamente como "especiais". Não há diferenças no escopo entre interpretadores e compiladores ANSI CL.

Common Lisp às vezes é chamado de Lisp-2 e Scheme de Lisp-1 , referindo-se ao uso de namespaces separados por CL para funções e variáveis. (Na verdade, CL tem muitos namespaces, como aqueles para tags go, nomes de bloco e palavras- loopchave). Há uma controvérsia de longa data entre os defensores do CL e do Scheme sobre as compensações envolvidas em vários namespaces. Em Scheme, é (amplamente) necessário evitar dar nomes de variáveis ​​que colidam com funções; As funções do esquema frequentemente têm argumentos chamados lis, lstou lystpara não entrar em conflito com a função do sistemalist. No entanto, em CL é necessário referir-se explicitamente ao namespace da função ao passar uma função como argumento – o que também é uma ocorrência comum, como no sortexemplo acima.

CL também difere de Scheme em sua manipulação de valores booleanos. Scheme usa os valores especiais #t e #f para representar verdade e falsidade. CL segue a antiga convenção Lisp de usar os símbolos T e NIL, com NIL representando também a lista vazia. Em CL, qualquer valor não-NIL é tratado como verdadeiro por condicionais, como if, enquanto em Scheme todos os valores não-#f são tratados como verdadeiros. Essas convenções permitem que alguns operadores em ambas as linguagens sirvam como predicados (respondendo a uma pergunta de valor booleano) e retornando um valor útil para computação adicional, mas em Scheme o valor '() que é equivalente a NIL em Common Lisp é avaliado como true em uma expressão booleana.

Por último, os documentos de padrões Scheme exigem otimização de chamada de cauda , ​​o que o padrão CL não faz. A maioria das implementações de CL oferece otimização de chamada de cauda, ​​embora geralmente apenas quando o programador usa uma diretiva de otimização. No entanto, o estilo de codificação CL comum não favorece o uso onipresente de recursão que o estilo Scheme prefere - o que um programador Scheme expressaria com recursão de cauda, ​​um usuário CL normalmente expressaria com uma expressão iterativa em do, dolist, loopou (mais recentemente) com o iteratepacote.

Implementações

Consulte as implementações do Lisp comum da categoria .

Common Lisp é definido por uma especificação (como Ada e C ) ao invés de uma implementação (como Perl ). Existem muitas implementações e as áreas de detalhes padrão nas quais elas podem diferir de maneira válida.

Além disso, as implementações tendem a vir com extensões, que fornecem funcionalidades não cobertas pelo padrão:

  • Nível superior interativo (REPL)
  • Depurador, Stepper e Inspetor
  • Estruturas de dados fracas (tabelas de hash)
  • Sequências extensíveis
  • LOOP extensível
  • Acesso ao ambiente
  • Protocolo de meta-objeto CLOS
  • Fluxos extensíveis baseados em CLOS
  • Sistema de Condição baseado em CLOS
  • Fluxos de rede
  • CLOS persistente
  • Suporte a Unicode
  • Interface de Língua Estrangeira (muitas vezes para C)
  • Interface do sistema operacional
  • Interface Java
  • Threads e Multiprocessamento
  • Entrega de aplicativos (aplicativos, bibliotecas dinâmicas)
  • Salvamento de imagens

Bibliotecas de software livre e de código aberto foram criadas para suportar extensões para Common Lisp de forma portátil, e são mais notavelmente encontradas nos repositórios do Common-Lisp.net [20] e CLOCC (Common Lisp Open Code Collection) [21 ] projetos.

As implementações de Common Lisp podem usar qualquer combinação de compilação de código nativo, compilação de código de byte ou interpretação. Common Lisp foi projetado para suportar compiladores incrementais, compiladores de arquivos e compiladores de blocos. Declarações padrão para otimizar a compilação (como função embutida ou especialização de tipo) são propostas na especificação da linguagem. A maioria das implementações do Common Lisp compila o código-fonte para o código de máquina nativo . Algumas implementações podem criar aplicativos independentes (otimizados). Outros compilam para bytecode interpretado, que é menos eficiente que o código nativo, mas facilita a portabilidade do código binário. Alguns compiladores compilam código Common Lisp para código C. O equívoco de que Lisp é uma linguagem puramente interpretada é mais provável porque os ambientes Lisp fornecem um prompt interativo e esse código é compilado um a um, de maneira incremental. Com Common Lisp, a compilação incremental é amplamente utilizada.

Algumas implementações baseadas em Unix ( CLISP , SBCL ) podem ser usadas como linguagem de script ; isto é, invocado pelo sistema de forma transparente da mesma forma que um interpretador de shell Perl ou Unix é. [22]

Lista de implementações

Implementações comerciais

Allegro Common Lisp
para Microsoft Windows, FreeBSD, Linux, Apple macOS e várias variantes do UNIX. O Allegro CL fornece um Ambiente de Desenvolvimento Integrado (IDE) (para Windows e Linux) e amplos recursos para entrega de aplicativos.
Lisp Comum Líquido
anteriormente chamado Lucid Common Lisp . Apenas manutenção, sem novos lançamentos.
LispWorks
para Microsoft Windows, FreeBSD, Linux, Apple macOS, iOS, Android e várias variantes do UNIX. O LispWorks fornece um Ambiente de Desenvolvimento Integrado (IDE) (disponível para a maioria das plataformas, mas não para iOS e Android) e amplos recursos para entrega de aplicativos.
modelo
para iOS, Android e macOS.
Gêneros abertos
para DEC Alfa.
Scieneer Common Lisp
que é projetado para computação científica de alto desempenho.

Implementações livremente redistribuíveis

Ceceio Comum do Urso Armado (ABCL)
Uma implementação de CL que é executada na Java Virtual Machine . [23] Inclui um compilador para código de byte Java e permite o acesso a bibliotecas Java a partir de CL. Antigamente era apenas um componente do Armed Bear J Editor .
CLISP
Uma implementação de compilação de bytecode, portátil e executada em vários sistemas Unix e semelhantes ao Unix (incluindo macOS ), bem como no Microsoft Windows e em vários outros sistemas.
Encerramento CL (CCL)
Originalmente um fork gratuito e de código aberto do Macintosh Common Lisp. Como essa história indica, o CCL foi escrito para Macintosh, mas o Clozure CL agora roda em macOS , FreeBSD , Linux , Solaris e Windows . As portas x86 de 32 e 64 bits são suportadas em cada plataforma. Além disso, existem portas Power PC para Mac OS e Linux. O CCL era anteriormente conhecido como OpenMCL, mas esse nome não é mais usado, para evitar confusão com a versão de código aberto do Macintosh Common Lisp.
CMUCL
Originalmente da Carnegie Mellon University , agora mantido como software livre e de código aberto por um grupo de voluntários. CMUCL usa um compilador de código nativo rápido. Está disponível em Linux e BSD para Intel x86; Linux para Alfa; macOS para Intel x86 e PowerPC; e Solaris, IRIX e HP-UX em suas plataformas nativas.
Corman Common Lisp
para Microsoft Windows. Em janeiro de 2015 Corman Lisp foi publicado sob licença do MIT. [24]
Lisp Comum Incorporável (ECL)
ECL inclui um interpretador e compilador de bytecode. Ele também pode compilar código Lisp para código de máquina por meio de um compilador C. ECL então compila o código Lisp para C, compila o código C com um compilador C e pode então carregar o código de máquina resultante. Também é possível incorporar ECL em programas C e código C em programas Common Lisp.
GNU Common Lisp (GCL)
Compilador Lisp do Projeto GNU . Ainda não totalmente compatível com ANSI, GCL é, no entanto, a implementação de escolha para vários grandes projetos, incluindo as ferramentas matemáticas Maxima , AXIOM e (historicamente) ACL2 . GCL roda em Linux em onze arquiteturas diferentes, e também em Windows, Solaris e FreeBSD .
Macintosh Common Lisp (MCL)
A versão 5.2 para computadores Apple Macintosh com processador PowerPC executando Mac OS X é de código aberto. O RMCL (baseado no MCL 5.2) é executado em computadores Apple Macintosh baseados em Intel usando o tradutor binário Rosetta da Apple.
ManKai Common Lisp (MKCL)
Uma filial da ECL . O MKCL enfatiza a confiabilidade, a estabilidade e a qualidade geral do código por meio de um sistema de tempo de execução altamente retrabalhado e multi-thread nativo. No Linux, o MKCL apresenta um sistema de tempo de execução totalmente compatível com POSIX.
Movitz
Implementa um ambiente Lisp para computadores x86 sem depender de nenhum sistema operacional subjacente.
Poplog
O Poplog implementa uma versão do CL, com POP-11 , e opcionalmente Prolog , e Standard ML (SML), permitindo programação de linguagem mista. Para todos, a linguagem de implementação é POP-11, que é compilada de forma incremental. Ele também possui um editor integrado do tipo Emacs que se comunica com o compilador.
Steel Bank Common Lisp (SBCL)
Um ramo da CMUCL . "De um modo geral, o SBCL se distingue do CMU CL por uma maior ênfase na manutenção." [25] SBCL roda nas plataformas CMUCL, exceto HP/UX; além disso, roda em Linux para AMD64, PowerPC, SPARC, MIPS, Windows x86 [26] e tem suporte experimental para rodar em Windows AMD64. A SBCL não usa um interpretador por padrão; todas as expressões são compiladas em código nativo, a menos que o usuário ative o interpretador. O compilador SBCL gera código nativo rápido de acordo com uma versão anterior do The Computer Language Benchmarks Game . [27]
Ufasoft Common Lisp
porte do CLISP para plataforma Windows com núcleo escrito em C++.

Outras implementações

Austin Kyoto Common Lisp
uma evolução do Kyoto Common Lisp por Bill Schelter
Ceceio Comum Borboleta
uma implementação escrita em Scheme para o computador multiprocessador BBN Butterfly [28] [29]
CLICC
um compilador Common Lisp para C [30]
CLOE
Lisp comum para PCs por Symbolics
Codemist Common Lisp
usado para a versão comercial do sistema de álgebra computacional Axiom [31] [32]
ExperCommon Lisp
uma implementação inicial para o Apple Macintosh pela ExperTelligence
Ceceio Comum Dourado
uma implementação para o PC pela GoldHill Inc. [33] [34]
Lisp Comum Ibuki
uma versão comercializada do Kyoto Common Lisp
Lisp comum de Kyoto
o primeiro compilador Common Lisp que usava C como linguagem de destino. GCL, ECL e MKCL originam-se desta implementação do Common Lisp.
eu
uma pequena versão do Common Lisp para sistemas embarcados desenvolvidos pela IS Robotics, agora iRobot [35]
Máquinas Lisp (da Symbolics , TI [36] [37] e Xerox [38] )
desde implementações de Common Lisp, além de seu dialeto Lisp nativo (Lisp Machine Lisp ou Interlisp). CLOS também estava disponível. Symbolics fornece uma versão aprimorada do Common Lisp. [39] [40] [41]
Procyon Common Lisp
uma implementação para Windows e Mac OS, usada por Franz para sua porta Windows de Allegro CL
LISP comum Star Sapphire
uma implementação para o PC
SubL
uma variante do Common Lisp usado para a implementação do sistema baseado em conhecimento Cyc [42]
Lisp comum de nível superior
uma implementação inicial para execução simultânea [43]
WCL
uma implementação de biblioteca compartilhada [44] [45]
VAX Common Lisp
Implementação da Digital Equipment Corporation que rodava em sistemas VAX rodando VMS ou ULTRIX
XLISP
uma implementação escrita por David Betz [46]

Aplicativos

Common Lisp é usado para desenvolver aplicativos de pesquisa (geralmente em Inteligência Artificial ), para desenvolvimento rápido de protótipos ou para aplicativos implantados.

Common Lisp é usado em muitos aplicativos comerciais, incluindo o Yahoo! Site de comércio na web da loja, que originalmente envolvia Paul Graham e mais tarde foi reescrito em C++ e Perl . [47] Outros exemplos notáveis ​​incluem:

Também existem aplicativos de código aberto escritos em Common Lisp, como:

Veja também

Referências

  1. ^ "Ação de Padrões ANSI - 28 de dezembro de 2018" (PDF) . ansi.org .
  2. ^ Citado da capa do padrão citado. ANSI INCITS 226-1994 [S2008], à venda na página do documento padrão Arquivado em 27 de setembro de 2020, na Wayback Machine .
  3. ^ "CLHS: Sobre o Common Lisp HyperSpec (TM)" . lispworks . com .
  4. ^ "CLHS: Seção 1.1.2" . lispworks . com .
  5. ^ "Implementação do Lisp comum: Uma pesquisa" . Arquivado a partir do original em 21 de abril de 2012 . Recuperado em 22 de dezembro de 2007 .
  6. ^ "Programas LISP antigos ainda são executados em Common Lisp" . Recuperado em 13 de maio de 2015 .
  7. ^ "Raízes de "Yu-Shiang Lisp", Correio de Jon L White, 1982" . cmu.edu .
  8. ^ "Índice de e-mail" . cl-su-ai.lisp.se .
  9. Knee-jerk Anti-LOOPism and other E-mail Phenomena: Oral, Written, and Electronic Patterns in Computer-Mediated Communication, JoAnne Yates e Wanda J. Orlikowski., 1993 Arquivado em 8 de agosto de 2012, no Wayback Machine
  10. ^ Jr, Steele; L, Guy (15 de agosto de 1982). Uma visão geral do COMMON LISP . Lfp '82. ACM. págs. 98–107. doi : 10.1145/800068.802140 . ISBN 9780897910828. S2CID  14517358 .
  11. ^ Reddy, Abhishek (22 de agosto de 2008). "Recursos do Common Lisp" .
  12. ^ "Suporte Unicode" . O Wiki Common Lisp . Recuperado em 21 de agosto de 2008 .
  13. ^ Richard P. Gabriel; Kent M. Pitman (junho de 1988). "Questões Técnicas de Separação em Células de Função e Células de Valor" . Lisp e Computação Simbólica . 1 (1): 81–101. doi : 10.1007/bf01806178 . S2CID 26716515 . 
  14. ^ "Common Lisp Hyperspec: Seção 3.1.7" .
  15. ^ "Common Lisp Hyperspec: Função FLOOR" .
  16. ^ "Common Lisp Hyperspec: Acessor GETHASH" .
  17. ^ "Let Over Lambda" . letoverlambda . com .
  18. ^ Peter Seibel (7 de abril de 2005). Lisp comum prático . Apress. ISBN 978-1-59059-239-7.
  19. ^ "Padrões de design em programação dinâmica" . norvig . com .
  20. ^ "Common-Lisp.net" .
  21. ^ "Coleção de código aberto do Lisp comum" .
  22. ^ "32.6. Entrega rápida com CLISP" . clisp.cons.org .
  23. ^ "Armed Bear Common Lisp" .
  24. ^ "Fontes do Corman Lisp já estão disponíveis" .
  25. ^ "História e Direitos Autorais" . Steel Bank Common Lisp .
  26. ^ "Mesa Plataforma" . Steel Bank Common Lisp .
  27. ^ "Quais programas são mais rápidos? - Jogo de benchmarks de linguagem de computador" . 20 de maio de 2013. Arquivado a partir do original em 20 de maio de 2013.
  28. ^ "Pacote: lang/lisp/impl/bbn/" . cs.cmu.edu .
  29. ^ "Desenvolvimentos recentes em Butterfly Lisp, 1987, AAAI Proceedings" (PDF) . aaai.org .
  30. ^ Burkart, O.; Goerigk, W.; Knutzen, H. (22 de junho de 1992). "CLICC: Uma Nova Abordagem para a Compilação de Programas Common Lisp para C". CiteSeerX 10.1.1.38.1282 .  {{cite journal}}:Cite journal requer |journal=( ajuda )
  31. ^ "codemist.co.uk" . lisp.codemist.co.uk .
  32. ^ "Axiom, o horizonte de 30 anos, página 43" (PDF) .
  33. ^ "Desenvolvedor Golden Common Lisp" . goldhill-inc . com .
  34. ^ Golden Common LISP: A Hands-On Approach, David J. Steele, junho de 2000 por Addison Wesley Publishing Company
  35. ^ Brooks, Rodney A.; ai., et (22 de Junho de 1995). "L – A Common Lisp para Sistemas Embarcados". CiteSeerX 10.1.1.2.1953 .  {{cite journal}}:Cite journal requer |journal=( ajuda )
  36. ^ "Conceitos de programação do TI Explorer" (PDF) .
  37. ^ "Referência de Lisp do Explorador de TI" (PDF) .
  38. ^ "Notas de lançamento do Medley Lisp" (PDF) .
  39. ^ "Symbolics Common Lisp Dictionary" (PDF) . trailing-edge . com .
  40. ^ "Conceitos comuns da língua do Lisp dos símbolos" (PDF) . trailing-edge . com .
  41. ^ "Construções de programação comuns de Symbolics Lisp" (PDF) . trailing-edge . com .
  42. ^ "Referência SubL - Cycorp" . cyc . com .
  43. ^ "Top Level Inc. - Grupo de preservação de software" . softwarepreservation.org .
  44. ^ WCL: Entregando aplicativos Common Lisp eficientes em Unix, Proceedings of the 1992 ACM conference on LISP and Functional Programming , Páginas 260–269
  45. ^ "commonlisp.net :: WCL" . pgc . com . Arquivado do original em 5 de abril de 2016 . Recuperado em 25 de março de 2016 .
  46. ^ "Pacote: lang/lisp/impl/xlisp/" . cs.cmu.edu .
  47. ^ "Batendo as médias" . paulgraham . com .
  48. ^ "Assistente do Autorizador" (PDF) . aaai.org .
  49. Assistente do Autorizador American Express Arquivado em 12 de dezembro de 2009, no Wayback Machine
  50. Desenvolvimento de aplicativos em tempo real Arquivado em 2 de agosto de 2016, no Wayback Machine . Gensym. Recuperado em 16 de agosto de 2016.
  51. ^ "Genworks GDL" .
  52. ^ "Opusmodus - Home" .
  53. PWGL – Home arquivado em 3 de maio de 2011, no Wayback Machine , recuperado em 17 de julho de 2013.
  54. ^ a b "Aeroespacial - Common Lisp" . lisp-lang.org .
  55. ^ "Usuários de piano, recuperados da página do fabricante" .
  56. ^ "Grammarly.com, Executando Lisp em Produção" .
  57. ^ "Agente Remoto" . ti.arc.nasa.gov .
  58. ^ "Lisping no JPL" .
  59. ^ "Aplicativos do cliente Franz Inc: NASA" . franz . com .
  60. ^ Sistema de planejamento e agendamento de pico . Stsci.edu. Recuperado em 17 de julho de 2013.
  61. ^ "Aplicações do cliente Franz Inc: Instituto do Telescópio Espacial" . franz . com .
  62. ^ "Como tudo começou…AKA o nascimento do CLR" . microsoft . com .
  63. ^ Huffman, Steve. "em lisp" . Aprovado . Arquivado do original em 17 de maio de 2018 . Recuperado em 11 de maio de 2019 .
  64. ^ "Pgloader" .
  65. ^ "Por que o pgloader é muito mais rápido" .

Bibliografia

Uma lista cronológica de livros publicados (ou prestes a serem publicados) sobre Common Lisp (a linguagem) ou sobre programação com Common Lisp (especialmente programação de IA).

Links externos