Common Lisp
Common Lisp (CL) é um dialeto da linguagem de programação Lisp, publicado no documento padrão ANSI ANSI INCITS 226-1994 (S20018) (anteriormente X3.226-1994 (R1999)). O Common Lisp HyperSpec, uma versão HTML com hiperlinks, foi derivado do padrão ANSI Common Lisp.
A linguagem Common Lisp foi desenvolvida como uma sucessora padronizada e aprimorada do Maclisp. No início dos anos 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. O Common Lisp procurou unificar, padronizar e estender os recursos desses dialetos do MacLisp. Common Lisp não é uma implementação, mas sim uma especificação de linguagem. 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. Common Lisp é uma linguagem de programação multiparadigma de uso geral. 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 de tempo de execução eficientes. Esse desenvolvimento incremental geralmente é feito de forma interativa sem interromper o aplicativo em execução.
Ele também oferece suporte à anotação e conversão de tipos opcionais, que podem ser adicionados 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, fixnum
pode conter um inteiro sem caixa em um intervalo suportado pelo hardware e pela implementação, permitindo uma aritmética mais eficiente do que inteiros grandes ou tipos de precisão arbitrária. Da mesma forma, o compilador pode ser informado por módulo ou por função qual tipo de nível de segurança é desejado, usando declarações optimize.
Common Lisp inclui CLOS, um sistema de objetos que suporta multimétodos e combinações de métodos. Muitas vezes é implementado com um protocolo Metaobject.
Common Lisp é extensível por meio de recursos padrão, como Macros Lisp (transformações de código) e Macros de leitura (analisadores de entrada para caracteres).
O Common Lisp fornece compatibilidade retroativa parcial com o Maclisp e o Lisp original de John McCarthy. Isso permite que software Lisp mais antigo seja portado para Common Lisp.
História
O trabalho no 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. Grande parte do design inicial da linguagem foi feito por correio eletrônico. 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.
A documentação da primeira 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 do 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, IO baseado em CLOS) foram fornecidas por implementações e bibliotecas.
Sintaxe
Common Lisp é um dialeto de Lisp. Ele usa S-expressões 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, produzindo 4. O nome da função é '+'. A Lisp não tem operadores como tal.
(Defvar *x *) ; Garante que exista uma variável *x*, ; sem dar-lhe um valor. Os asteriscos fazem parte de ; o nome, por convenção denotando uma variável especial (global). ; O símbolo *x* também é dotado com a propriedade que ; as ligações subsequentes dele são dinâmicas, em vez de lexical. (setf *x * 4/2.2) ; Define a variável *x* para o valor de ponto flutuante 42.1
;; Definir uma função que define um número: (defuntos quadrado quadrado (x) (* x x)
;; Executar a função: (quadrado quadrado 3) ; Retorno 9
; A construção 'let' cria um escopo para variáveis locais. Aqui. ;; a variável 'a' é vinculada a 6 e a variável 'b' é vinculada ; a 4. Dentro da 'let' está um 'body', onde o último valor computado é retornado. ; Aqui o resultado da adição de um e b é retornado da expressão 'let'. ; As variáveis a e b têm escopo lexical, a menos que os símbolos tenham sido ;; marcado como variáveis especiais (por exemplo, por DEFVAR prévia). (Deixa-me. (um 6) (b) 4) (+ um b)) ; retorna 10
Tipos de dados
O Lisp comum tem muitos tipos de dados.
Tipos escalares
Os tipos incluem números inteiros, proporções, números de ponto flutuante e números complexos. O Common Lisp usa bignums para representar valores numéricos de tamanho e precisão arbitrários. O tipo de razão representa exatamente as frações, uma instalação não disponível em vários idiomas. O Lisp comum coescia automaticamente os valores numéricos entre esses tipos, conforme apropriado.
O tipo de caractere comum não se limita a caracteres ASCII. A maioria das implementações modernas permite caracteres unicode.
O tipo é comum a idiomas Lisp, mas amplamente desconhecido fora deles. Um símbolo é um objeto de dados exclusivo e nomeado com várias partes: nome, valor, função, lista de propriedades e pacote. Destes, a célula Value Cell e Célula de função são os mais importantes. Os símbolos no LISP são frequentemente usados de maneira semelhante aos identificadores em outros idiomas: 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 se avaliam, por exemplo, todos os símbolos do pacote de palavras-chave são auto-avaliados. Os valores booleanos em Lisp comum são representados pelos símbolos auto-avaliados t e nil. O Common Lisp possui espaços 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 Round
arredonda o argumento para o número inteiro mais próximo, com casos intermediários arredondados para o número inteiro uniforme. As funções truncaram
, piso
e teto
em direção a zero, para baixo ou para cima, respectivamente. Todas essas funções retornam a parte fracionária descartada como um valor secundário. Por exemplo, (piso -2,5)
produz -3, 0,5; (teto -2,5)
produz −2, -0,5; (Rodada 2.5)
Rende 2, 0,5; e (rodada 3.5)
Gosta 4, -0,5.
Estruturas de dados
Tipos de sequência em lisp comuns 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 Lisp, as listas em Lisp comum são compostas de Conses , às vezes chamadas de células Contras ou . Um contras é uma estrutura de dados com dois slots, chamada seu carro e cdr . Uma lista é uma cadeia vinculada de conses ou a lista vazia. Cada carro de cada Contras refere -se a um membro da lista (possivelmente outra lista). Cada CDR de cada Contras refere -se aos próximos contras - exceto os últimos contras em uma lista, cujo CDR se refere ao valor nil
. Os conses também podem ser facilmente usados para implementar árvores e outras estruturas de dados complexas; Embora geralmente seja aconselhado a usar instâncias de estrutura ou classe. Também é possível criar estruturas de dados circulares com conses.
Lisp comum suporta matrizes multidimensionais e pode redimensionar dinamicamente matrizes ajustáveis , se necessário. Matrizes multidimensionais podem ser usadas para matemática da matriz. Um vetor é uma matriz unidimensional. As matrizes podem transportar qualquer tipo como membros (mesmo tipos mistos na mesma matriz) ou podem ser especializados para conter um tipo específico de membros, como em um vetor de bits. Geralmente, apenas alguns tipos são suportados. Muitas implementações podem otimizar as funções da matriz quando a matriz usada é especializada em tipo. Dois tipos de matriz especializados por tipo são padrão: a string é um vetor de caracteres, enquanto um bit-vetor é um vetor de bits.
Tabelas de hash Associações de lojas entre objetos de dados. Qualquer objeto pode ser usado como chave ou valor. As tabelas de hash são redimensionadas automaticamente conforme necessário.
Os 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.
Estruturas , similar em uso às estruturas C e registros Pascal, representam estruturas de dados complexos arbitrários com qualquer número e tipo de campos (chamados slots ). As estruturas permitem uma excitação única.
As classes são semelhantes às estruturas, mas oferecem recursos mais dinâmicos e ineferição múltipla. (Veja Clos). As aulas foram adicionadas atrasadas ao Lisp comum e há alguma sobreposição conceitual com estruturas. Os objetos criados de classes são chamados de instâncias . Um caso especial são funções genéricas. Funções genéricas são funções e instâncias.
funções
O LISP comum suporta funções de primeira classe. Por exemplo, é possível escrever funções que assumam outras funções como argumentos ou funções de retorno também. Isso torna possível descrever operações muito gerais.
A biblioteca Lisp comum depende muito de funções de ordem superior. Por exemplo, a função classy
toma um operador relacional como um argumento e a 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 as estruturas de dados de acordo com uma chave.
; Classifica a lista usando a função > e < como operador relacional. (Tipo (lista 5 2 6 3 1 4) # '>) ; Retornos (6 5 4 3 2 1) (Tipo (lista 5 2 6 3 1 4) # '<) ; Retornos (1 2 3 4 5 6)
; Classifica a lista de acordo com o primeiro elemento de cada sub-lista. (Tipo (lista '(9 A) '(3 B) '(4 C) # '< : # 'Primeiro primeiro.) ; Retornos (3 B) (4 C) (9 A)
O modelo de avaliação para funções é muito simples. Quando o avaliador encontra um formulário (f a1 a2...)
, ele presume que o símbolo chamado f é um dos seguintes:
- Um operador especial (facilmente verificado contra uma lista fixa)
- Um operador de macro (deve ter sido definido anteriormente)
- O nome de uma função (padrão), que pode ser um símbolo, ou um sub-forma começando com o símbolo
lambda
.
Se f é o nome de uma função, os argumentos A1, A2,..., e são avaliados em ordem da esquerda para a direita, e a função é encontrada e invocada com os valores fornecidos como parâmetros.
Definindo funções
O macro defun define funções em que uma definição de função fornece o nome da função, os nomes de quaisquer argumentos e um corpo de função:
(defuntos quadrado quadrado (x) (* x x)
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 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:
(defuntos quadrado quadrado (x) "Calcula o quadrado do único-float x." (declarada (único-float x) (otimizar (velocidade de velocidade 3) (depuração 0) (segurança 1) (o único-float (* x x)
Funções anônimas (literais de função) são definidas usando expressões lambda
, por exemplo (lambda (x) (* x x))
para uma função que eleva ao quadrado seu argumento. O estilo de programação Lisp freqüentemente usa funções de ordem superior para as quais é útil fornecer funções anônimas como argumentos.
Funções locais podem ser definidas com flet
e labels
.
(flecha (quadrado quadrado (x) (* x x) (quadrado 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 operador compile
. (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 defgeneric
define funções genéricas. Funções genéricas são uma coleção de métodos.
A macro defmethod
define 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 Adicionar (um b))
(O que foi? Adicionar (um número) (b) número) (+ um b))
(O que foi? Adicionar (um vetor) (b) número) (mapa Vencedor (Lambda (n) (+ n b)) um)
(O que foi? Adicionar (um vetor) (b) vetor) (mapa Vencedor # '+ um b))
(O que foi? Adicionar (um string) (b) string) (concatenar "string" um b))
(Adicionar 2 3) ; devoluções 5 (Adicionar #1 2 3 4) 7) ; retorna #(8 9 10 11) (Adicionar #1 2 3 4) #4 3 2 1) ; retorna #(5 5 5 5) (Adicionar "COMMON" "LISP") ; retorna "COMMON LISP"
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ção é 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
, labels
, defmethod
e defgeneric
.
Para passar uma função pelo nome como argumento para outra função, deve-se usar o operador especial função
, comumente abreviado como #'
. O primeiro exemplo de sort
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 operador funcall
no argumento.
O modelo de avaliação do Scheme é mais simples: existe 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 os programadores mais experientes no outro. Por exemplo, muitos programadores Common Lisp gostam de usar nomes descritivos de variáveis como list ou string, o que pode causar problemas no Scheme, pois eles sombreiam localmente os nomes das funções.
Se um namespace separado para funções é uma vantagem é uma fonte de discórdia na comunidade Lisp. Geralmente é referido como o debate Lisp-1 vs. Lisp-2. Lisp-1 refere-se ao modelo Scheme e Lisp-2 refere-se ao modelo Common Lisp. Esses nomes foram cunhados em um artigo de 1988 de Richard P. Gabriel e Kent Pitman, que compara extensivamente as duas abordagens.
Vários valores de retorno
Common Lisp suporta o conceito de valores múltiplos, onde qualquer expressão sempre tem um único valor primário, mas também pode ter qualquer número de valores secundários, que pode ser recebido e inspecionado por chamadores interessados. Esse conceito é diferente do retorno de um valor de lista, pois os valores secundários são totalmente opcionais e passados por meio de um canal lateral dedicado. Isso significa que os chamadores podem permanecer totalmente inconscientes da presença dos valores secundários, caso não precisem deles, e torna conveniente usar o mecanismo para comunicar informações que às vezes são úteis, mas nem sempre necessárias. Por exemplo,
- O
TRUNCATE
função rodeia o número dado a um inteiro para zero. No entanto, também retorna um restante como um valor secundário, tornando-se muito fácil determinar qual valor foi truncado. Ele também suporta um parâmetro divisor opcional, que pode ser usado para executar a divisão Euclidean trivialmente:
(Deixa-me. (x 126677) (Sim. 458) (multi-valor-bind (- Sim. restante) (truncas x Sim.) (formato n "A dividido por ~A é ~A restante ~A" x Sim. - Sim. restante);;;;; => "1266778 dividido por 458 é 2765 restante 408"
GETHASH
retorna o valor de uma chave em um mapa associativo, ou o valor padrão de outra forma, 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 o padrão pode simplesmente usá-lo como é, mas quando tal 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 é desnecessariamente sobrecarregado ou limitado pelo outro. Ter esse recurso no nível da linguagem remove a necessidade de verificar a existência da chave ou compará-la com null como seria feito em outros idiomas.
(defuntos Esgoto (biblioteca) (Não! "Obrigado" biblioteca 42)(defuntos A resposta é: (biblioteca) (formato n "A resposta é ~A" (Esgoto biblioteca);;;; Retorno "A resposta é 42" se ANSWER não estiver presente no LIBRARY(defuntos o-resposta-2 (biblioteca) (multi-valor-bind (resposta Claro.) (Esgoto biblioteca) (se (não Claro.) "Não sei" (formato n "A resposta é ~A" resposta)); Retorna "Eu não sei" se ANSWER não presente no LIBRARY
Vários valores são suportados por um punhado de formulários padrão, sendo os mais comuns o formulário especial MULTIPLE-VALUE-BIND
para acessar valores secundários e VALUES
para retornar vários valores:
(defuntos Oito bolas mágicas () "Reverter uma previsão de visão, com a probabilidade como um valor secundário" (valores "Sai bem" (aleatório 1.0.);;;; => "Sai bem";;;;; => 0.3187
Outros tipos
Outros tipos de dados em Common Lisp incluem:
- Nomes de caminho representar arquivos e diretórios no sistema de arquivos. A instalação Common Lisp Pathname é mais geral do que as convenções de nomes de arquivos da maioria dos sistemas operacionais, tornando o acesso dos programas Lisp aos arquivos amplamente portátil em diversos sistemas.
- Entrada e saída fluxos representam fontes e pias de dados binários ou textuais, como o terminal ou arquivos abertos.
- O Common Lisp tem um gerador de números pseudo-aleatório integrado (PRNG). Estado aleatório objetos representam fontes reutilizáveis de números pseudo-aleatórios, permitindo que o usuário seme o PRNG ou fazer com que ele replay 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.
- Aulas são objetos de primeira classe, e são eles próprios instâncias de classes chamadas classes metaobject (metaclasses para curto).
- Mesas de leitura são um tipo de objeto que controla como o leitor de Lisp Comum analisa o texto do código fonte. Ao controlar qual tabela de leitura está em uso quando o código é lido, o programador pode mudar 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.
O escopo refere-se ao conjunto de circunstâncias em que um nome é determinado para ter uma vinculação específica.
Determinadores de 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, ele se refere a um operador especial ou uma macro ou função de ligação, caso contrário a uma variável de ligação ou algo mais.
- o tipo de expressão em que a referência ocorre. Por exemplo,
(go x)
significa controle de transferência para etiquetax
, Considerando que(print x)
refere-se à variávelx
. Ambos os escopos dex
pode ser ativo na mesma região do texto do programa, uma vez que as etiquetas estão em um namespace separado de nomes variáveis. Uma forma especial ou forma macro tem controle completo sobre os significados de todos os símbolos em sua sintaxe. Por exemplo,(defclass x (a b) ())
, uma definição de classe, o(a b)
é uma lista de classes básicas, então esses nomes são procurados no espaço de nomes de classe, ex
não é uma referência a uma ligação existente, mas o nome de uma nova classe derivada dea
eb
. Estes fatos emergem puramente da semântica dedefclass
. O único fato genérico sobre esta expressão é quedefclass
refere-se a uma ligação de macro; tudo o mais é atédefclass
. - a localização da referência dentro do texto do programa. Por exemplo, se uma referência à variável
x
é fechado em um construto de ligação, como uma deixa que define uma ligação parax
, então a referência está no âmbito criado por essa ligação. - para uma referência variável, se um símbolo variável foi, local ou globalmente, declarado especial. Isso determina se a referência é resolvida dentro de um ambiente lexical, ou dentro de 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 ligações. Cada tipo de referência usa seu próprio tipo de ambiente. As referências a variáveis lexical são resolvidas em um ambiente lexical, et cetera. Mais de um ambiente pode estar 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 lexical do ambiente.
Para entender a que um símbolo se refere, o programador Common Lisp deve saber que tipo de referência está sendo expressa, que tipo de escopo ele usa se for uma referência variável (escopo dinâmico versus léxico) e também o tempo de execução situação: em que ambiente a referência é resolvida, onde o binding foi introduzido 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. Bindings estabelecidos neste ambiente possuem extensão dinâmica, o que significa que um binding é estabelecido no início da execução de alguma construção, como um bloco let
, e desaparece quando essa construção termina de ser executada: seu tempo de vida é vinculado à ativação e desativação dinâmica de um bloco. No entanto, uma vinculaçã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. Bindings 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 daquele bloco) são ditas 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 vinculações também têm escopo definido dinamicamente, como reinicializações e tags catch. Ligações de função não podem ser definidas dinamicamente usando flet
(que fornece apenas ligações de função com escopo lexical), mas objetos de função (um objeto de primeiro nível em Common Lisp) podem ser atribuídos a variáveis com escopo definido dinamicamente, vinculados usando let
no escopo dinâmico, então chamado usando funcall
ou APPLY
.
O escopo dinâmico é extremamente útil porque adiciona clareza referencial e disciplina às variáveis globais. As 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 secretos e ad hoc entre os módulos que levam a interações inesperadas e inesperadas.
No 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 ligaçã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. Às vezes, isso é chamado de "religação" a variável. Vincular uma variável com escopo definido dinamicamente cria temporariamente um novo local de memória para essa variável e associa o nome a esse local. Enquanto essa vinculação estiver em vigor, todas as referências a essa variável referem-se à nova vinculação; a vinculação anterior está oculta. Quando a execução da expressão de ligação termina, o local da memória temporária desaparece e a ligação antiga é revelada, com o valor original intacto. Obviamente, várias ligações dinâmicas para a mesma variável podem ser aninhadas.
Em implementações Common Lisp que suportam multithreading, os escopos dinâmicos são específicos para cada thread de execução. Assim, as variáveis especiais servem como uma abstração para o armazenamento local da thread. Se um encadeamento religar uma variável especial, essa religação não terá efeito nessa variável em outros encadeamentos. O valor armazenado em uma ligação só pode ser recuperado pelo thread que criou essa ligação. Se cada encadeamento ligar alguma variável especial *x*
, então *x*
se comportará como armazenamento local de encadeamento. 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 sob recursão: a religação de variável dinâmica cuida disso. E essa variável deve ser local de thread (ou então um mutex grande deve ser usado) para que o esquema não seja interrompido em threads: as 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 escreva na saída padrão:
(defuntos Foo () (formato ) "Olá, mundo")
Para capturar sua saída em uma string de caracteres, *standard-output* pode ser vinculado a um fluxo de string e chamado:
(com saída para corda (* saída padrão*) (Foo)
- Sim. "Olá, mundo"; a saída reunida retornou como uma string
Léxico
Common Lisp suporta ambientes lexicais. 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. Escopo léxico significa que a visibilidade é fisicamente restrita ao bloco no qual a vinculação é estabelecida. As referências que não são textualmente (isto é, lexicalmente) incorporadas nesse bloco simplesmente não veem essa ligação.
As tags em um TAGBODY possuem escopo léxico. A expressão (GO X) é errônea se não estiver embutida em um TAGBODY que contém um rótulo X. Porém, os vínculos do 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 lexical, é inválido para o corpo desse encerramento tentar transferir o controle para uma tag via GO:
(Defvar *Apanhado*) ;; manterá uma função (- Não. (setf *Apanhado* (Lambda () (Vai. algum rótulo) (Vai. rótulo final) ;; pular o (impressão "Olá") algum rótulo (impressão "Olá") rótulo final) - Sim. NIL
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, ignorando o código (print "Hello"). Como o rótulo final está no final do tagbody, o tagbody termina, resultando em NIL. Suponha que a função lembrada anteriormente seja agora chamada:
(- Sim. *Apanhado*) ;; Erro!
Esta situação é errônea. A resposta de uma implementação é uma condição de erro contendo a mensagem "GO: tagbody para tag SOME-LABEL já foi deixado". A função tentou avaliar (go some-label), que está lexicamente incorporada no tagbody e resolve para o rótulo. No entanto, o tagbody não está executando (sua extensão terminou) e, portanto, a transferência de controle não pode ocorrer.
Os vínculos de funções locais em Lisp têm escopo léxico e os vínculos de variáveis 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 vinculação de variável é estabelecida, essa vinculação continua a existir enquanto forem possíveis referências a ela, mesmo após o término da construção que estabeleceu essa vinculação. 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 a encerramentos lexicais.
A ligação lexical é o modo de ligação padrão para variáveis Common Lisp. Para um símbolo individual, ele pode ser alterado 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 Common Lisp que variáveis especiais (ou seja, com escopo dinâmico) tenham nomes que começam e terminam com um símbolo de asterisco *
no que é chamado de "convenção protetor de orelha". Se aderida, esta convenção efetivamente cria um namespace separado para variáveis especiais, de modo que as variáveis destinadas a serem léxicas não se tornem acidentalmente especiais.
O escopo lexical é ú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 empilhar o armazenamento, portanto, abrir e fechar escopos lexicais tem sobrecarga mínima. Mesmo nos casos em que devem ser gerados fechamentos completos, o acesso ao ambiente do fechamento ainda é eficiente; normalmente cada variável torna-se um deslocamento em um vetor de ligações e, portanto, uma referência de variável torna-se uma simples instrução 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 léxico 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 para o X vinculado 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 estabeleça um vínculo 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 impedir que isso aconteça também é uma vantagem; conseqüentemente, o Common Lisp suporta escopo léxico e dinâmico.
Macros
Uma macro em Lisp se assemelha superficialmente a uma função em uso. Porém, ao invés de representar uma expressão que é avaliada, representa uma transformação do código-fonte do programa. A macro obtém a fonte que envolve como argumentos, vincula-os a seus parâmetros e calcula um novo formulário de origem. 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 looping, construções de ramificação)
- construções de escopo e encadernação
- sintaxe simplificada para código fonte complexo e repetido
- formas de definição de nível superior com efeitos colaterais de tempo compilado
- programação orientada a dados
- idiomas específicos de domínio incorporado (exemplos: SQL, HTML, Prolog)
- formas de finalização implícitas
Vários recursos padrão do Common Lisp também precisam ser implementados como macros, como:
- o padrão
setf
abstração, para permitir expansões personalizadas de tempo de compilação de operadores de atribuição / acesso with-accessors
,with-slots
,with-open-file
e outros semelhantesWITH
macros- Dependendo da implementação,
if
oucond
é uma macro construída sobre o outro, o operador especial;when
eunless
consiste em macros - O poderoso
loop
linguagem específica do domínio
Macros são definidos pela macro defmacro. O operador especial macrolet permite a definição de macros locais (com escopo léxico). Também é possível definir macros para símbolos usando define-symbol-macro e symbol-macrolet.
O livro On Lisp de Paul Graham 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
Macros permitem que programadores Lisp criem novas formas sintáticas na linguagem. Um uso típico é criar novas estruturas de controle. A macro de exemplo fornece uma construção de loop until
. A sintaxe é:
(até o formulário de teste*)
A definição de macro para até:
(O que é? até (teste de teste > corpo) (Deixa-me. (start-tag (Gensym "Start") (ponto final (Gensym "END") "(- Não. ,start-tag (quando ,teste de teste (Vai. ,ponto final) (Progn ,@corpo) (Vai. ,start-tag) ,ponto final)
tagbody é um operador especial primitivo Common Lisp que fornece a capacidade de nomear tags e usar o formulário go para pular para essas tags. A aspas ` fornece uma notação que fornece modelos de código, onde o valor dos formulários precedidos por uma vírgula são preenchidos. Formulários precedidos por vírgula e arroba são unidos. O formulário tagbody testa a condição final. Se a condição for verdadeira, ele pula para a tag final. Caso contrário, o código do corpo fornecido é executado e, em seguida, pula para a tag inicial.
Um exemplo de uso da macro until 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 se parece com isso:
(TAGBODY # START1136 (QUANDO (ZEROP (RANDOM 10.) (Vai. #) (PROGRAMA (WRITE-LINE "Olá") (Vai. # START1136) #)
Durante a expansão da macro, o valor da variável teste é (= (random 10) 0) e o valor da variável corpo é ((escrever linha "Olá")). O corpo é uma lista de formas.
Símbolos geralmente são invertidos automaticamente. A expansão usa o TAGBODY com dois rótulos. Os símbolos para esses rótulos são calculados pelo GENSYM e não estão contidos em nenhum pacote. Dois formulários go usam essas tags para pular. Já que 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 quando, que também será expandida. A expansão completa de um formulário de origem é chamada de code walking.
Na forma totalmente expandida (caminhou), a forma quando é substituída pela primitiva se:
(TAGBODY # START1136 (IF (ZEROP (RANDOM 10.) (PROGRAMA (Vai. #) NIL) (PROGRAMA (WRITE-LINE "Olá") (Vai. # START1136) #)
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 abstrata, 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 Common Lisp (ou de terceiros) disponível.
Captura variável e sombreamento
Macros Lisp comuns são capazes do que é comumente chamado de captura de variável, onde os símbolos no corpo de expansão da macro coincidem com aqueles no contexto de chamada, permitindo ao programador criar macros em que vários símbolos têm significado. O termo captura de variável é um tanto enganoso, porque todos os namespaces são vulneráveis a capturas indesejadas, incluindo o namespace do operador e da função, o namespace do rótulo tagbody, tag catch, manipulador de condição e namespaces de reinicialização.
Acaptura de variável pode apresentar defeitos de software. Isso acontece de uma das duas maneiras a seguir:
- Em primeiro lugar, uma expansão macro pode inadvertidamente fazer uma referência simbólica que o escritor macro assumiu resolverá em um namespace global, mas o código onde a macro é expandida acontece para fornecer uma definição local e sombreada que rouba essa referência. Que isto seja referido como captura tipo 1.
- A segunda maneira, captura tipo 2, é exatamente o oposto: alguns dos argumentos da macro são pedaços de código fornecido pelo chamador de macro, e essas peças de código são escritas de tal forma que fazem referências a ligações circundantes. No entanto, a macro insere essas peças de código em uma expansão que define suas próprias ligações que captura acidentalmente algumas dessas referências.
O dialeto Scheme de Lisp fornece um sistema de escrita de macro que fornece a transparência referencial que elimina ambos os tipos de problemas de captura. Este tipo de sistema macro é às vezes chamado de "higiênico", em particular por seus proponentes (que consideram anti-higiênicos sistemas macro que não resolvem automaticamente este problema).
No Common Lisp, a higiene de macro é assegurada de duas maneiras diferentes.
Uma abordagem é usar gensyms: símbolos únicos garantidos que podem ser usados em uma expansão macro 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 o uso de gensyms. Gensyms resolvem a captura do tipo 2 facilmente, mas não são aplicáveis à captura do tipo 1 da mesma forma, porque a expansão 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 prejudicial 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 lida com captura tipo 1 e tipo 2.
No entanto, os pacotes não resolvem a captura do tipo 1 de referências a funções e operadores Common Lisp padrão. 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). Considerando que os símbolos da biblioteca Common Lisp são externos e frequentemente importados ou tornados visíveis em pacotes definidos pelo usuário.
A seguir, um exemplo de captura indesejada no namespace do operador, ocorrendo na expansão de uma macro:
;; expansão do UNTIL faz uso liberal de DO (O que é? até (expressão > corpo) "(do () (,expressão) ,@corpo) ;; macrolet estabelece ligação do operador lexical para DO (macro (do (...) ... Alguma coisa? mais ...) (até (= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = (aleatório 10.) 0) (Linha de escrita "Olá")
A macro until
se expandirá em um formulário que chama do
, que se destina a se referir à macro Common Lisp padrão do
. No entanto, neste contexto, do
pode ter um significado completamente diferente, então until
pode não funcionar corretamente.
O Common Lisp resolve o problema do sombreamento de operadores e funções padrão proibindo sua redefinição. Porque ele redefine o operador padrão do
, o anterior é na verdade um fragmento de Common Lisp não conforme, que permite implementações para diagnosticá-lo e rejeitá-lo.
Sistema de condições
O sistema de condições é responsável pelo tratamento de exceções em Common Lisp. Ele fornece condições, manipuladors e reinicializaçãos. Conditions são objetos que descrevem uma situação excepcional (por exemplo, um erro). Se uma condição for sinalizada, o sistema Common Lisp procura por um manipulador para este tipo de condição e chama o manipulador. O manipulador agora pode procurar reinícios e usar um desses reinícios 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 o método apropriado função reiniciar.
Essas reinicializações, se não forem 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 total 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 variável dinâmica *debugger-hook*
. O código encontrado nos formulários unwind-protect, 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 uma função Lisp teste chamada do Read-Eval-Print-LOOP (REPL), quando o arquivo não existir. O sistema Lisp apresenta quatro reinicializações. O usuário seleciona a reinicialização Repetir ABRIR usando um 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 erro. Todo o tratamento de erros e o código de reinicialização são fornecidos 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: POEN-LOCAL-LMFS-1
Arg 0: #P"lispm:>zippy>lispm-int.lisp.newest"
s-A, : Retentar OPEN de lispm:>zippy>lispm-int.lisp.newest
s-B: Retira OPEN usando um nome de caminho diferente
s-C, : Retornar para Lisp Nível superior em um servidor TELNET
s-D: Processo de reinício terminal TELNET
- Sim. Retira OPEN usando um nome de caminho diferente
Use o nome do caminho em vez [padrão 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. Peter Norvig explica quantos Design Patterns são mais simples de implementar em uma linguagem dinâmica com os recursos do CLOS (Múltipla Herança, Mixins, Multimétodos, Metaclasses, Combinações de Métodos, etc.). Várias extensões para Common Lisp para programação orientada a objeto foram propostas para serem incluídas no padrão ANSI Common Lisp, mas eventualmente o CLOS foi adotado como o sistema de objeto padrão para Common Lisp. CLOS é um sistema de objeto dinâmico com despacho múltiplo e 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 alterações 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 alterações de classe e a classe de objetos pode ser alterada.
CLOS foi integrado ao ANSI Common Lisp. As 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 de tipo Common Lisp. Muitos tipos 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. O Common Lisp permite que 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 implementavam escopo léxico no compilador e escopo dinâmico no interpretador. O Common Lisp requer que tanto o interpretador quanto o compilador usem o 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. O 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.
Existe também uma função para avaliar o código Lisp: eval
. eval
usa o código como s-expressões 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 construir listas e símbolos e então este código pode ser avaliado com a função eval
. Várias implementações Common Lisp (como Clozure CL e SBCL) estão implementando eval
usando seu compilador. Desta forma, o código é compilado, embora seja avaliado usando a função eval
.
O compilador de arquivo é invocado usando a função compile-file. O arquivo gerado com o código compilado é chamado de arquivo fasl (de carregamento rápido). Esses arquivos fasl e também os arquivos de código-fonte podem ser carregados com a função load em um sistema Common Lisp em execução. Dependendo da implementação, o compilador de arquivo gera código de byte (por exemplo, para a Java Virtual Machine), código de linguagem C (que é compilado com um compilador C) ou, diretamente, código nativo.
As implementações 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 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 para atender especialmente ao desenvolvimento interativo; por exemplo, defvar
apenas atribuirá um valor à sua variável fornecida se ainda não estiver vinculada, enquanto defparameter
sempre executará a atribuição. Essa distinção é útil ao avaliar, compilar e carregar o código de forma interativa 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 diretamente manipuláveis pelo código do usuário. O operador especial progv
permite criar associações lexicais 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 seguinte programa 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 de ano + 365)(defuntos aniversário-paradoxo (probabilidade número de pessoas) (Deixa-me. (nova probabilidade (* (/ (- Não. + tamanho de ano + número de pessoas) + tamanho de ano +) probabilidade) (se (< nova 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 >
23
Classificando uma lista de objetos pessoais
Definimos uma classe person
e um método para exibir o nome e a idade de uma pessoa.
Em seguida, definimos um grupo de pessoas como uma lista de objetos person
.
Em seguida, iteramos sobre a lista classificada.
(defclass pessoa () (Nome : : Nome : acesso nome de pessoa) (idade : : idade : acesso pessoa-idade) (:documentação "A classe PERSON com slots NOME e AGE.")(O que foi? exibição de exibição (objeto pessoa) fluxo) "Displaying a PERSON object to an output stream." (com lotes (Nome idade) objeto (formato fluxo "A" Nome idade)(desmontagem * Grupo * (lista (insatisfação 'pessoa : Nome "Bob" : idade 33) (insatisfação 'pessoa : Nome "Chris" : idade 16.) (insatisfação 'pessoa : Nome "Ash" : idade 23) "Uma lista de objetos PERSON.")(lista de tarefas (pessoa (Tipo (Lista de cópia * Grupo *) # '> : # 'pessoa-idade) (exibição de exibição pessoa * saída padrão*) (Terceirizado)
Imprime os três nomes com idade decrescente.
Bob (33)
Cinza (23)
(16)
Exponenciando por quadratura
O uso da macro LOOP é demonstrado:
(defuntos poder (x n) (loop com resultado = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 1 enquanto (Abertura n) quando (Oddp n) do (setf resultado (* resultado x) do (setf x (* x x) n (truncas n 2) finalmente (retorno resultado)
Exemplo de uso:
CL-USER > (poder 2 200)160693804425802755419620941162602520237827928351376
Compare com a exponenciação incorporada:
CL-USER > (= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = (ex. 2 200) (poder 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.
(defuntos lista de linhas (arquivo predicado) "Retorna uma lista de linhas em arquivo, para a qual o predicado aplicado a a linha retorna T." (com o arquivo aberto (fluxo arquivo) (loop para linha de linha = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = (Linha de leitura fluxo n n) enquanto linha de linha quando (- Sim. predicado linha de linha) recolher É ele.)
A função AVAILABLE-SHELLS chama a função LIST-MATCHING-LINES acima 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 de arquivo de um shell).
(defuntos pedreiras disponíveis (> (arquivo #p"/etc/shells") (lista de linhas arquivo (Lambda (linha de linha) (e (Abertura (comprimento linha de linha) (Charlie. (Charlie. linha de linha 0) #/) (nome do caminho (string-right-trim '(#space #) linha de linha)))
Exemplos de resultados (no Mac OS X 10.6):
CL-USER > (pedreiras disponíveis)(#P"/bin/bash" #P"/bin/csh" #P"/bin/ksh" #P"/bin/sh" #P"/bin/tcsh" #P"/bin/zsh")
Comparação com outros Lisps
O Common Lisp é mais frequentemente comparado e contrastado com o Scheme—no mínimo porque são os dois dialetos Lisp mais populares. O Scheme é anterior ao CL e vem não apenas da mesma tradição do Lisp, mas de alguns dos mesmos engenheiros - Guy Steele, com quem Gerald Jay Sussman projetou o Scheme, presidiu o comitê de padrões do 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 designs contribuíram para o Common Lisp—como ZetaLisp e Franz Lisp—usaram 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 lexical para Lisp; uma inspiração do ALGOL 68. A CL também oferece suporte a variáveis com escopo dinâmico, mas elas devem ser explicitamente declaradas como "especiais". Não há diferenças no escopo entre os interpretadores e compiladores ANSI CL.
Common Lisp às vezes é chamado de Lisp-2 e Scheme de Lisp-1, referindo-se ao uso de CL de espaços de nomes separados para funções e variáveis. (Na verdade, CL tem muitos namespaces, como aqueles para tags go, nomes de bloco e palavras-chave loop
). Há uma longa controvérsia 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 conflitam com funções; As funções de esquema frequentemente têm argumentos chamados lis
, lst
ou lyst
para não entrar em conflito com a função do sistema list
. No entanto, em CL é necessário referir-se explicitamente ao namespace da função ao passar uma função como um argumento — o que também é uma ocorrência comum, como no exemplo sort
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 Esquema 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 como retornando um valor útil para computação posterior, mas em Scheme o valor '() que é equivalente a NIL em Common Lisp é avaliado como verdadeiro em uma expressão booleana.
Por fim, os documentos de padrões do Esquema exigem otimização de chamada final, o que o padrão CL não exige. A maioria das implementações de CL oferece otimização de chamada final, 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 geralmente expressaria com uma expressão iterativa em do
, dolist
, loop
ou (mais recentemente) com o pacote iterate
.
Implementações
Consulte as implementações Common Lisp 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 validamente.
Além disso, as implementações tendem a vir com extensões, que fornecem funcionalidades não cobertas pelo padrão:
- Top-Level interativo (REPL)
- Coleção de lixo
- Depurador, Stepper e Inspector
- Estruturas de dados fracas (tabelas de cinza)
- Sequências extensíveis
- LOOP extensível
- Acesso ao ambiente
- CLOS Protocolo Meta-object
- CLOS com base em fluxos extensíveis
- Sistema de Condição baseado em CLOS
- Fluxos de rede
- CLOS persistentes
- Suporte Unicode
- Interface de Língua Estrangeira (muitas vezes para C)
- Interface do Sistema Operacional
- Interface Java
- Linhas e Multiprocessamento
- Entrega de aplicativos (aplicações, bibliotecas dinâmicas)
- Economia de imagens
Bibliotecas de software livre e de código aberto foram criadas para oferecer suporte a extensões para Common Lisp de forma portátil e são encontradas principalmente nos repositórios dos projetos Common-Lisp.net e CLOCC (Common Lisp Open Code Collection).
As implementações 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 arquivo e compiladores de bloco. Declarações padrão para otimizar a compilação (como inlining de função ou especialização de tipo) são propostas na especificação da linguagem. A maioria das implementações Common Lisp compilam o código-fonte para o código de máquina nativo. Algumas implementações podem criar aplicativos autônomos (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 o código Common Lisp para o 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 o Common Lisp, a compilação incremental é amplamente usada.
Algumas implementações baseadas em Unix (CLISP, SBCL) podem ser usadas como uma linguagem de script; isto é, invocado pelo sistema de forma transparente da mesma forma que um interpretador de shell Perl ou Unix é.
Lista de implementações
Implementações comerciais
- Lisp comum de Allegro
- para Microsoft Windows, FreeBSD, Linux, Apple macOS e várias variantes UNIX. Allegro CL fornece um ambiente de desenvolvimento integrado (IDE) (para Windows e Linux) e amplas capacidades para entrega de aplicativos.
- Lisp líquido comum
- anteriormente chamado Lucid Common Lisp. Apenas manutenção, sem novas versões.
- LispWorks
- para Microsoft Windows, FreeBSD, Linux, Apple macOS, iOS, Android e várias variantes UNIX. LispWorks fornece um Ambiente de Desenvolvimento Integrado (IDE) (disponível para a maioria das plataformas, mas não para iOS e Android) e amplas capacidades para entrega de aplicativos.
- Molhadores
- para iOS, Android e macOS.
- Abrir Genera
- para DEC Alpha.
- Lisp comum de Scieneer
- que é projetado para computação científica de alto desempenho.
Implementações livremente redistribuíveis
- Urso Armado Lisp Comum (ABCL)
- Uma implementação CL que é executada na Java Virtual Machine. Ele inclui um compilador de código byte Java e permite acesso a bibliotecas Java da CL. Foi anteriormente apenas um componente do Editor do Urso Armado J.
- Fecho
- Uma implementação baseada em LLVM que interopera perfeitamente com bibliotecas C++. Executa em vários sistemas Unix e Unix (incluindo macOS).
- CLISP
- Uma implementação de compilação por bytecode, portátil e roda em vários sistemas Unix e Unix (incluindo macOS), bem como Microsoft Windows e vários outros sistemas.
- Clozure CL (CCL)
- Originalmente um garfo livre e de código aberto de Macintosh Common Lisp. Como esse histórico implica, o CCL foi escrito para o Macintosh, mas o Clozure CL agora é executado no 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. CCL era anteriormente conhecido como OpenMCL, mas esse nome não é mais usado, para evitar confusão com a versão open source de Macintosh Common Lisp.
- CMUCL
- Originalmente da Carnegie Mellon University, agora mantida 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 Alpha; macOS para Intel x86 e PowerPC; e Solaris, IRIX e HP-UX em suas plataformas nativas.
- Corman Lisp comum
- para Microsoft Windows. Em janeiro de 2015 Corman Lisp foi publicado sob licença MIT.
- Lisp comum embutido (ECL)
- ECL inclui um interpretador de bytecode e compilador. Ele também pode compilar o código Lisp para o código da máquina através de um compilador C. ECL então compila Lisp código 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 comuns Lisp.
- GNU Common Lisp (GCL)
- O compilador Lisp do Projeto GNU. Ainda não totalmente compatível com a ANSI, a GCL é, no entanto, a implementação da escolha para vários grandes projetos, incluindo as ferramentas matemáticas Maxima, AXIOM e (historicamente) ACL2. O GCL é executado em Linux sob onze arquiteturas diferentes, e também em Windows, Solaris e FreeBSD.
- Macintosh Lisp Comum (MCL)
- Versão 5.2 para computadores Apple Macintosh com um processador PowerPC executando Mac OS X é código aberto. RMCL (baseado no MCL 5.2) é executado em computadores Apple Macintosh baseados na Intel usando o tradutor binário Rosetta da Apple.
- Lisp comum de ManKai (MKCL)
- Um ramo da ECL. O MKCL enfatiza a confiabilidade, a estabilidade e a qualidade geral do código através de um sistema de tempo de execução altamente retrabalhado, nativamente multithreaded. No Linux, o MKCL possui um sistema de tempo de execução compatível com POSIX.
- Movitz
- Implementa um ambiente Lisp para computadores x86 sem depender de qualquer sistema operacional subjacente.
- Pai.
- Poplog implementa uma versão de CL, com POP-11, e opcionalmente Prolog, e ML padrão (SML), permitindo a programação de linguagem mista. Para todos, a linguagem de implementação é POP-11, que é compilado incrementalmente. Ele também tem um editor integrado como Emacs que se comunica com o compilador.
- Banco de aço Lisp comum (SBCL)
- Um ramo da CMUCL. "De um modo geral, o SBCL distingue-se do CMU CL por uma maior ênfase na manutenção." O SBCL é executado nas plataformas CMUCL, exceto HP/UX; além disso, é executado no Linux para AMD64, PowerPC, SPARC, MIPS, Windows x86 e tem suporte experimental para ser executado no Windows AMD64. O SBCL não usa um interpretador por padrão; todas as expressões são compiladas para código nativo a menos que o usuário alterne o interpretador. O compilador SBCL gera código nativo rápido de acordo com uma versão anterior do The Computer Language Benchmarks Game.
- Ufasoft Common Lisp
- porta de CLISP para plataforma janelas com núcleo escrito em C++.
Outras implementações
- Austin Kyoto Lisp
- uma evolução de Kyoto Common Lisp por Bill Schelter
- Lisp comum de borboleta
- uma implementação escrita em Scheme para o computador multiprocessador BBN Butterfly
- CLICC
- um Lisp comum para compilador C
- CLORO
- Lisp comum para PCs por simbologia
- Codemist Lisp comum
- usado para a versão comercial do sistema de álgebra computador Axiom
- Experador de Lisp
- uma implementação antecipada para o Apple Macintosh por ExperTelligence
- Lispado comum de ouro
- uma implementação para o PC pela GoldHill Inc.
- Lispo comum de Ibuki
- uma versão comercializada de Kyoto Common Lisp
- Quioto Lisp
- o primeiro compilador de Lisp Comum que usou C como uma língua-alvo. GCL, ECL e MKCL originam-se desta implementação de Lisp Comum.
- L
- uma pequena versão do Common Lisp para sistemas embarcados desenvolvidos pela IS Robotics, agora iRobot
- Máquinas de Lisp (de Symbolics, TI e Xerox)
- forneceu implementações de Lisp Comum além de seu dialeto Lisp nativo (Lisp Machine Lisp ou Interlisp). CLOS também estava disponível. Symbolics fornece uma versão aprimorada Common Lisp.
- Procyon Lisp comum
- uma implementação para Windows e Mac OS, usado por Franz para a sua porta do Windows de Allegro CL
- Safira estrela comum LISP
- uma implementação para o PC
- SubL
- uma variante do Lisp Comum usado para a implementação do sistema baseado no conhecimento Cyc
- Nível superior Lisp comum
- uma implementação precoce para execução simultânea
- WCL
- uma implementação de biblioteca compartilhada
- VAX Lisp comum
- Implementação da Digital Equipment Corporation que funcionou em sistemas VAX com VMS ou ULTRIX
- XLISP
- uma implementação escrita por David Betz
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! Store, que originalmente envolvia Paul Graham e mais tarde foi reescrito em C++ e Perl. Outros exemplos notáveis incluem:
- ACT-R, uma arquitetura cognitiva usada em um grande número de projetos de pesquisa.
- Assistente do Autorizador, um grande sistema baseado em regras usado pelo American Express, analisando pedidos de crédito.
- Cyc, um projeto de longo prazo para criar um sistema baseado em conhecimento que fornece uma enorme quantidade de conhecimento de senso comum.
- Gensym G2, um sistema de especialistas em tempo real e mecanismo de regras de negócios
- Genworks GDL, baseado no kernel Gendl de código aberto.
- O ambiente de desenvolvimento Jak e Daxter série de jogos de vídeo, desenvolvido por Naughty Dog.
- ITA O motor de busca de tarifas baixas da Software, usado por sites de viagem, como Orbitz e Kayak.com e companhias aéreas como American Airlines, Continental Airlines e US Airways.
- Mirai, uma suite gráfica 3D. Foi usado para animar a cara de Gollum no filme Senhor dos Anéis: as duas torres.
- Opusmodus é um sistema de composição musical baseado no Common Lisp, usado na composição assistida por computador.
- Prototype Verification System (PVS), um ambiente mecanizado para especificação formal e verificação.
- PWGL é um ambiente de programação visual sofisticado baseado no Common Lisp, usado na composição assistida por computador e síntese sonora.
- Piano, um conjunto completo de análise de aeronaves, escrito em Common Lisp, usado por empresas como Boeing, Airbus e Northrop Grumman.
- Grammarly, uma plataforma de escrita em inglês, tem seu motor de gramática principal escrito em Common Lisp.
- A Ferramenta de Análise Dinâmica e Replanejamento (DART), que se diz que só tem pago durante os anos de 1991 a 1995 para todos os trinta anos de investimentos da DARPA na pesquisa de IA.
- O Jet Propulsion Lab da NASA "Deep Space 1", um premiado programa Common Lisp para piloto automático da nave espacial Deep Space One.
- SigLab, uma plataforma comum de Lisp para processamento de sinais usada em defesa de mísseis, construída por Raytheon.
- Sistema de Planejamento Missionário Mars Pathfinder da NASA.
- SPIKE, um sistema de agendamento para a Terra ou observatórios e satélites baseados no espaço, notavelmente o Telescópio Espacial Hubble, escrito em Lisp Comum.
- O Common Lisp foi usado para prototipar o coletor de lixo da Microsoft. NET Common Language Runtime (em inglês).
- A versão original do Reddit, embora os desenvolvedores mais tarde mudou para Python devido à falta de bibliotecas para Common Lisp, de acordo com um post oficial do co-fundador do Reddit Steve Huffman.
Também existem aplicativos de código aberto escritos em Common Lisp, como:
- ACL2, um testador de teorema automatizado completo para uma variante aplicativa do Common Lisp.
- Axiom, um sistema de álgebra de computador sofisticado.
- Maxima, um sistema de álgebra de computador sofisticado, baseado em Macsyma.
- OpenMusic, um ambiente de programação visual orientado a objetos baseado no Common Lisp, usado na composição assistida por computador.
- Pgloader, um carregador de dados para PostgreSQL, que foi reescrito de Python para Common Lisp.
- Stumpwm, um tiling, teclado conduzido X11 Window Manager escrito inteiramente no Common Lisp.
Contenido relacionado
Telecomunicações na Estônia
Dia (software)
Telecomunicações em Antígua e Barbuda