LISP - Guia Rápido

John McCarthy inventou o LISP em 1958, logo após o desenvolvimento do FORTRAN. Ele foi implementado pela primeira vez por Steve Russell em um computador IBM 704.

É particularmente adequado para programas de Inteligência Artificial, pois processa informações simbólicas de forma eficaz.

O Common Lisp surgiu, durante as décadas de 1980 e 1990, na tentativa de unificar o trabalho de vários grupos de implementação que foram sucessores do Maclisp, como ZetaLisp e NIL (New Implementation of Lisp) etc.

Ele serve como uma linguagem comum, que pode ser facilmente estendida para implementação específica.

Os programas escritos em Common LISP não dependem de características específicas da máquina, como comprimento de palavra, etc.

Características do Common LISP

  • É independente da máquina

  • Ele usa metodologia de design iterativo e fácil extensibilidade.

  • Permite atualizar os programas dinamicamente.

  • Ele fornece depuração de alto nível.

  • Ele fornece programação orientada a objetos avançada.

  • Ele fornece um sistema macro conveniente.

  • Ele fornece uma ampla variedade de tipos de dados como objetos, estruturas, listas, vetores, matrizes ajustáveis, tabelas de hash e símbolos.

  • É baseado em expressão.

  • Ele fornece um sistema de condição orientado a objetos.

  • Ele fornece uma biblioteca de E / S completa.

  • Ele fornece estruturas de controle extensas.

Aplicativos construídos em LISP

Grandes aplicativos de sucesso construídos em Lisp.

  • Emacs

  • G2

  • AutoCad

  • Igor Engraver

  • Yahoo Store

Configuração de ambiente local

Se você ainda deseja configurar seu ambiente para a linguagem de programação Lisp, você precisa dos dois softwares a seguir disponíveis em seu computador, (a) Editor de Texto e (b) Executador Lisp.

Editor de texto

Isso será usado para digitar seu programa. Exemplos de poucos editores incluem o bloco de notas do Windows, o comando Editar do sistema operacional, Brief, Epsilon, EMACS e vim ou vi.

O nome e a versão do editor de texto podem variar em diferentes sistemas operacionais. Por exemplo, o Bloco de notas será usado no Windows e o vim ou vi pode ser usado no Windows, bem como no Linux ou UNIX.

Os arquivos que você cria com seu editor são chamados de arquivos-fonte e contêm o código-fonte do programa. Os arquivos fonte para programas Lisp são normalmente nomeados com a extensão ".lisp"

Antes de iniciar sua programação, certifique-se de ter um editor de texto instalado e de ter experiência suficiente para escrever um programa de computador, salvá-lo em um arquivo e finalmente executá-lo.

O executor Lisp

O código-fonte escrito no arquivo-fonte é a fonte legível para o seu programa. Ele precisa ser "executado", para se transformar em linguagem de máquina para que sua CPU possa realmente executar o programa de acordo com as instruções fornecidas.

Esta linguagem de programação Lisp será usada para executar seu código-fonte no programa executável final. Presumo que você tenha conhecimento básico sobre uma linguagem de programação.

CLISP é o compilador multi-arquitetônico GNU Common LISP usado para configurar o LISP no Windows. A versão do Windows emula um ambiente Unix usando MingW no Windows. O instalador cuida disso e adiciona clisp automaticamente à variável PATH do Windows.

Você pode obter o CLISP mais recente para Windows aqui - https://sourceforge.net/projects/clisp/files/latest/download

Ele cria um atalho no Menu Iniciar por padrão, para o intérprete linha por linha.

Como usar CLISP

Durante a instalação, clisp é adicionado automaticamente à sua variável PATH se você selecionar a opção (RECOMENDADO). Isso significa que você pode simplesmente abrir uma nova janela do Prompt de Comando e digitar “clisp” para abrir o compilador.

Para executar um arquivo * .lisp ou * .lsp, basta usar -

clisp hello.lisp

As expressões LISP são chamadas de expressões simbólicas ou expressões S. As s-expressões são compostas por três objetos válidos, átomos, listas e strings.

Qualquer expressão s é um programa válido.

Os programas LISP são executados em um interpreter ou como compiled code.

O interpretador verifica o código-fonte em um loop repetido, também chamado de loop de leitura-avaliação-impressão (REPL). Ele lê o código do programa, avalia-o e imprime os valores retornados pelo programa.

Um programa simples

Vamos escrever uma expressão s para encontrar a soma dos três números 7, 9 e 11. Para fazer isso, podemos digitar no prompt do interpretador.

(+ 7 9 11)

LISP retorna o resultado -

27

Se você deseja executar o mesmo programa como um código compilado, crie um arquivo de código-fonte LISP denominado myprog.lisp e digite o seguinte código nele.

(write (+ 7 9 11))

Ao clicar no botão Executar ou digitar Ctrl + E, o LISP o executa imediatamente e o resultado retornado é -

27

LISP usa notação de prefixo

Você deve ter notado que o LISP usa prefix notation.

No programa acima, o símbolo + funciona como o nome da função para o processo de soma dos números.

Na notação de prefixo, os operadores são escritos antes de seus operandos. Por exemplo, a expressão,

a * ( b + c ) / d

será escrito como -

(/ (* a (+ b c) ) d)

Vamos dar outro exemplo, vamos escrever o código para converter a temperatura Fahrenheit de 60 o F para a escala centígrada -

A expressão matemática para esta conversão será -

(60 * 9 / 5) + 32

Crie um arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(write(+ (* (/ 9 5) 60) 32))

Quando você clica no botão Executar ou digita Ctrl + E, o LISP o executa imediatamente e o resultado retornado é -

140

Avaliação de programas LISP

A avaliação dos programas LISP tem duas partes -

  • Tradução do texto do programa em objetos Lisp por um programa leitor

  • Implementação da semântica da linguagem em termos desses objetos por um programa avaliador

O processo de avaliação segue as seguintes etapas -

  • O leitor traduz as sequências de caracteres para objetos LISP ou s-expressions.

  • O avaliador define a sintaxe do Lisp formsque são construídos a partir de expressões S. Este segundo nível de avaliação define uma sintaxe que determina quals-expressions são formulários LISP.

  • O avaliador funciona como uma função que assume um formato LISP válido como argumento e retorna um valor. Esta é a razão pela qual colocamos a expressão LISP entre parênteses, pois estamos enviando toda a expressão / formulário para o avaliador como argumentos.

O programa 'Hello World'

Aprender uma nova linguagem de programação não decola até que você aprenda como saudar o mundo inteiro nessa linguagem, certo!

Portanto, crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(write-line "Hello World")

(write-line "I am at 'Tutorials Point'! Learning LISP")

Ao clicar no botão Executar ou digitar Ctrl + E, o LISP o executa imediatamente e o resultado retornado é -

Hello World

I am at 'Tutorials Point'! Learning LISP

Blocos de construção básicos em LISP

Os programas LISP são compostos de três blocos básicos de construção -

  • atom
  • list
  • string

A atomé um número ou sequência de caracteres contíguos. Inclui números e caracteres especiais.

A seguir estão exemplos de alguns átomos válidos -

hello-from-tutorials-point
name
123008907
*hello*
Block#221
abc123

UMA list é uma sequência de átomos e / ou outras listas entre parênteses.

A seguir estão exemplos de algumas listas válidas -

( i am a list)
(a ( a b c) d e fgh)
(father tom ( susan bill joe))
(sun mon tue wed thur fri sat)
( )

UMA string é um grupo de caracteres colocados entre aspas duplas.

A seguir estão exemplos de algumas strings válidas -

" I am a string"
"a ba c d efg #$%^&!"
"Please enter the following details :"
"Hello from 'Tutorials Point'! "

Adicionando comentários

O símbolo de ponto e vírgula (;) é usado para indicar uma linha de comentário.

Por exemplo,

(write-line "Hello World") ; greet the world

; tell them your whereabouts

(write-line "I am at 'Tutorials Point'! Learning LISP")

Ao clicar no botão Executar ou digitar Ctrl + E, o LISP o executa imediatamente e o resultado retornado é -

Hello World

I am at 'Tutorials Point'! Learning LISP

Alguns pontos notáveis ​​antes de passar para a próxima

A seguir estão alguns dos pontos importantes a serem observados -

  • As operações numéricas básicas em LISP são +, -, * e /

  • LISP representa uma chamada de função f (x) como (fx), por exemplo cos (45) é escrito como cos 45

  • As expressões LISP não diferenciam maiúsculas de minúsculas, cos 45 ou COS 45 são iguais.

  • O LISP tenta avaliar tudo, incluindo os argumentos de uma função. Apenas três tipos de elementos são constantes e sempre retornam seu próprio valor

    • Numbers

    • A carta t, isso significa verdade lógica.

    • O valor que nil, isso significa lógico falso, bem como uma lista vazia.

Um pouco mais sobre formulários LISP

No capítulo anterior, mencionamos que o processo de avaliação do código LISP segue os seguintes passos.

  • O leitor traduz as sequências de caracteres para objetos LISP ou s-expressions.

  • O avaliador define a sintaxe do Lisp formsque são construídos a partir de expressões S. Este segundo nível de avaliação define uma sintaxe que determina quais s-expressões são formas LISP.

Agora, um formulário LISP poderia ser.

  • Um átomo
  • Um vazio ou não lista
  • Qualquer lista que tenha um símbolo como primeiro elemento

O avaliador funciona como uma função que assume um formato LISP válido como argumento e retorna um valor. Esta é a razão pela qual colocamos oLISP expression in parenthesis, porque estamos enviando toda a expressão / formulário para o avaliador como argumentos.

Convenções de nomenclatura em LISP

O nome ou os símbolos podem consistir em qualquer número de caracteres alfanuméricos que não sejam espaços em branco, parênteses de abertura e fechamento, aspas simples e duplas, barra invertida, vírgula, dois pontos, ponto e vírgula e barra vertical. Para usar esses caracteres em um nome, você precisa usar o caractere de escape (\).

Um nome pode ter dígitos, mas não inteiramente de dígitos, porque então seria lido como um número. Da mesma forma, um nome pode ter pontos, mas não pode ser feito inteiramente de pontos.

Uso de aspas simples

LISP avalia tudo, incluindo os argumentos da função e membros da lista.

Às vezes, precisamos interpretar átomos ou listas literalmente e não queremos que sejam avaliados ou tratados como chamadas de função.

Para fazer isso, precisamos preceder o átomo ou a lista com uma aspa simples.

O exemplo a seguir demonstra isso.

Crie um arquivo denominado main.lisp e digite o seguinte código nele.

(write-line "single quote used, it inhibits evaluation")
(write '(* 2 3))
(write-line " ")
(write-line "single quote not used, so expression evaluated")
(write (* 2 3))

Ao clicar no botão Executar ou digitar Ctrl + E, o LISP o executa imediatamente e o resultado retornado é -

single quote used, it inhibits evaluation
(* 2 3) 
single quote not used, so expression evaluated
6

No LISP, as variáveis ​​não são digitadas, mas os objetos de dados sim.

Os tipos de dados LISP podem ser categorizados como.

  • Scalar types - por exemplo, tipos de número, caracteres, símbolos etc.

  • Data structures - por exemplo, listas, vetores, vetores de bits e strings.

Qualquer variável pode receber qualquer objeto LISP como seu valor, a menos que você o tenha declarado explicitamente.

Embora não seja necessário especificar um tipo de dados para uma variável LISP, no entanto, isso ajuda em certas expansões de loop, em declarações de métodos e algumas outras situações que discutiremos em capítulos posteriores.

Os tipos de dados são organizados em uma hierarquia. Um tipo de dados é um conjunto de objetos LISP e muitos objetos podem pertencer a um desses conjuntos.

o typep predicado é usado para descobrir se um objeto pertence a um tipo específico.

o type-of função retorna o tipo de dados de um determinado objeto.

Especificadores de tipo em LISP

Especificadores de tipo são símbolos definidos pelo sistema para tipos de dados.

matriz fixnum pacote string simples
átomo flutuador nome do caminho vetor simples
bignum função estado aleatório flutuação única
mordeu mesa de hash Razão standard-char
bit-vetor inteiro racional corrente
personagem palavra chave legível corda
[comum] Lista seqüência [string-char]
função compilada flutuação longa flutuação curta símbolo
complexo nada byte assinado t
contras nulo array simples byte sem sinal
flutuação dupla número vetor de bit simples vetor

Além desses tipos definidos pelo sistema, você pode criar seus próprios tipos de dados. Quando um tipo de estrutura é definido usandodefstruct função, o nome do tipo de estrutura torna-se um símbolo de tipo válido.

Exemplo 1

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(setq x 10)
(setq y 34.567)
(setq ch nil)
(setq n 123.78)
(setq bg 11.0e+4)
(setq r 124/2)

(print x)
(print y)
(print n)
(print ch)
(print bg)
(print r)

Ao clicar no botão Executar ou digitar Ctrl + E, o LISP o executa imediatamente e o resultado retornado é -

10 
34.567 
123.78 
NIL 
110000.0 
62

Exemplo 2

A seguir, vamos verificar os tipos de variáveis ​​usadas no exemplo anterior. Crie um novo arquivo de código-fonte denominado main. lisp e digite o seguinte código nele.

(defvar x 10)
(defvar y 34.567)
(defvar ch nil)
(defvar n 123.78)
(defvar bg 11.0e+4)
(defvar r 124/2)

(print (type-of x))
(print (type-of y))
(print (type-of n))
(print (type-of ch))
(print (type-of bg))
(print (type-of r))

Ao clicar no botão Executar ou digitar Ctrl + E, o LISP o executa imediatamente e o resultado retornado é -

(INTEGER 0 281474976710655) 
SINGLE-FLOAT 
SINGLE-FLOAT 
NULL 
SINGLE-FLOAT 
(INTEGER 0 281474976710655)

As macros permitem estender a sintaxe do LISP padrão.

Tecnicamente, uma macro é uma função que recebe uma expressão s como argumentos e retorna um formulário LISP, que é então avaliado.

Definindo uma Macro

No LISP, uma macro nomeada é definida usando outra macro chamada defmacro. A sintaxe para definir uma macro é -

(defmacro macro-name (parameter-list))
"Optional documentation string."
body-form

A definição da macro consiste no nome da macro, uma lista de parâmetros, uma string de documentação opcional e um corpo de expressões Lisp que definem o trabalho a ser executado pela macro.

Exemplo

Vamos escrever uma macro simples chamada setTo10, que pegará um número e definirá seu valor para 10.

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(defmacro setTo10(num)
(setq num 10)(print num))
(setq x 25)
(print x)
(setTo10 x)

Ao clicar no botão Executar ou digitar Ctrl + E, o LISP o executa imediatamente e o resultado retornado é -

25
10

No LISP, cada variável é representada por um symbol. O nome da variável é o nome do símbolo e é armazenado na célula de armazenamento do símbolo.

Variáveis ​​globais

As variáveis ​​globais têm valores permanentes em todo o sistema LISP e permanecem em vigor até que um novo valor seja especificado.

Variáveis ​​globais são geralmente declaradas usando o defvar construir.

Por exemplo

(defvar x 234)
(write x)

Ao clicar no botão Executar ou digitar Ctrl + E, o LISP o executa imediatamente e o resultado retornado é

234

Uma vez que não há declaração de tipo para variáveis ​​no LISP, você especifica diretamente um valor para um símbolo com o setq construir.

Por exemplo

->(setq x 10)

A expressão acima atribui o valor 10 à variável x. Você pode se referir à variável usando o próprio símbolo como uma expressão.

o symbol-value A função permite que você extraia o valor armazenado no local de armazenamento do símbolo.

Por exemplo

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(setq x 10)
(setq y 20)
(format t "x = ~2d y = ~2d ~%" x y)

(setq x 100)
(setq y 200)
(format t "x = ~2d y = ~2d" x y)

Ao clicar no botão Executar ou digitar Ctrl + E, o LISP o executa imediatamente e o resultado retornado é.

x = 10 y = 20 
x = 100 y = 200

Variáveis ​​Locais

Variáveis ​​locais são definidas dentro de um determinado procedimento. Os parâmetros nomeados como argumentos em uma definição de função também são variáveis ​​locais. As variáveis ​​locais são acessíveis apenas dentro da respectiva função.

Como as variáveis ​​globais, as variáveis ​​locais também podem ser criadas usando o setq construir.

Existem duas outras construções - let e prog para criar variáveis ​​locais.

A construção let tem a seguinte sintaxe.

(let ((var1  val1) (var2  val2).. (varn  valn))<s-expressions>)

Onde var1, var2, ..varn são nomes de variáveis ​​e val1, val2, .. valn são os valores iniciais atribuídos às respectivas variáveis.

Quando leté executado, cada variável recebe o respectivo valor e, por último, a expressão s é avaliada. O valor da última expressão avaliada é retornado.

Se você não incluir um valor inicial para uma variável, ele é atribuído a nil.

Exemplo

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(let ((x 'a) (y 'b)(z 'c))
(format t "x = ~a y = ~a z = ~a" x y z))

Ao clicar no botão Executar ou digitar Ctrl + E, o LISP o executa imediatamente e o resultado retornado é.

x = A y = B z = C

o prog construção também tem a lista de variáveis ​​locais como seu primeiro argumento, que é seguido pelo corpo do prog, e qualquer número de expressões-s.

o prog função executa a lista de s-expressões em sequência e retorna nil a menos que encontre uma chamada de função chamada return. Então o argumento do return função é avaliada e retornada.

Exemplo

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(prog ((x '(a b c))(y '(1 2 3))(z '(p q 10)))
(format t "x = ~a y = ~a z = ~a" x y z))

Ao clicar no botão Executar ou digitar Ctrl + E, o LISP o executa imediatamente e o resultado retornado é.

x = (A B C) y = (1 2 3) z = (P Q 10)

No LISP, constantes são variáveis ​​que nunca mudam seus valores durante a execução do programa. Constantes são declaradas usando odefconstant construir.

Exemplo

O exemplo a seguir mostra a declaração de uma constante global PI e posteriormente o uso desse valor dentro de uma função chamada area-circle que calcula a área de um círculo.

o defun construção é usada para definir uma função, vamos examiná-la no Functions capítulo.

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(defconstant PI 3.141592)
(defun area-circle(rad)
   (terpri)
   (format t "Radius: ~5f" rad)
   (format t "~%Area: ~10f" (* PI rad rad)))
(area-circle 10)

Ao clicar no botão Executar ou digitar Ctrl + E, o LISP o executa imediatamente e o resultado retornado é.

Radius:  10.0
Area:   314.1592

Um operador é um símbolo que informa ao compilador para executar manipulações matemáticas ou lógicas específicas. O LISP permite inúmeras operações em dados, suportadas por várias funções, macros e outras construções.

As operações permitidas nos dados podem ser categorizadas como -

  • Operaçoes aritimeticas
  • Operações de Comparação
  • Operações Lógicas
  • Operações bit a bit

Operaçoes aritimeticas

A tabela a seguir mostra todos os operadores aritméticos suportados pelo LISP. Assumir variávelA contém 10 e variável B detém 20 então -

Show Examples

Operador Descrição Exemplo
+ Adiciona dois operandos (+ AB) dará 30
- Subtrai o segundo operando do primeiro (- AB) dará -10
* Multiplica ambos os operandos (* AB) dará 200
/ Divide numerador por de-numerador (/ BA) dará 2
mod, rem Operador de Módulo e o restante após uma divisão inteira (mod BA) dará 0
incf O operador de incrementos aumenta o valor inteiro pelo segundo argumento especificado (incf A 3) dará 13
decf Operador de decrementos diminui o valor inteiro pelo segundo argumento especificado (decf A 4) dará 9

Operações de Comparação

A tabela a seguir mostra todos os operadores relacionais suportados pelo LISP que comparam entre números. No entanto, ao contrário dos operadores relacionais em outras linguagens, os operadores de comparação LISP podem ter mais de dois operandos e funcionam apenas com números.

Assumir variável A contém 10 e variável B contém 20, então -

Show Examples

Operador Descrição Exemplo
= Verifica se os valores dos operandos são todos iguais ou não, se sim então a condição torna-se verdadeira. (= AB) não é verdade.
/ = Verifica se os valores dos operandos são todos diferentes ou não, se os valores não são iguais a condição torna-se verdadeira. (/ = AB) é verdadeiro.
> Verifica se os valores dos operandos estão diminuindo monotonicamente. (> AB) não é verdade.
< Verifica se os valores dos operandos estão aumentando monotonicamente. (<AB) é verdadeiro.
> = Verifica se o valor de algum operando à esquerda é maior ou igual ao valor do próximo operando à direita. Se sim, a condição se torna verdadeira. (> = AB) não é verdade.
<= Verifica se o valor de algum operando esquerdo é menor ou igual ao valor de seu operando direito; em caso positivo, a condição se torna verdadeira. (<= AB) é verdadeiro.
max Ele compara dois ou mais argumentos e retorna o valor máximo. (AB máx.) retorna 20
min Ele compara dois ou mais argumentos e retorna o valor mínimo. (min AB) retorna 10

Operações lógicas em valores booleanos

O LISP comum fornece três operadores lógicos: and, or, e notque opera em valores booleanos. PresumirA tem valor nulo e B tem valor 5, então -

Show Examples

Operador Descrição Exemplo
e Leva qualquer número de argumentos. Os argumentos são avaliados da esquerda para a direita. Se todos os argumentos forem avaliados como não nulo, o valor do último argumento será retornado. Caso contrário, nil é retornado. (e AB) retornará NIL.
ou Leva qualquer número de argumentos. Os argumentos são avaliados da esquerda para a direita até que um seja avaliado como não nulo, nesse caso o valor do argumento é retornado, caso contrário ele retornanil. (ou AB) retornará 5.
não Leva um argumento e retorna t se o argumento for avaliado como nil. (não A) retornará T.

Operações bit a bit em números

Operadores bit a bit trabalham em bits e executam operações bit a bit. As tabelas de verdade para operações bit a bit and, or, e xor são as seguintes -

Show Examples

p q p e q p ou q p xor q
0 0 0 0 0
0 1 0 1 1
1 1 1 1 0
1 0 0 1 1
Assume if A = 60; and B = 13; now in binary format they will be as follows:
A = 0011 1100
B = 0000 1101
-----------------
A and B = 0000 1100
A or B = 0011 1101
A xor B = 0011 0001
not A  = 1100 0011

Os operadores bit a bit suportados pelo LISP estão listados na tabela a seguir. Assumir variávelA detém 60 e variável B detém 13, então -

Operador Descrição Exemplo
logand Isso retorna o AND lógico bit a bit de seus argumentos. Se nenhum argumento for fornecido, o resultado será -1, que é uma identidade para esta operação. (log e ab)) dará 12
logior Isso retorna o OR INCLUSIVE lógico bit a bit de seus argumentos. Se nenhum argumento for fornecido, o resultado será zero, que é uma identidade para esta operação. (logior ab) dará 61
logxor Isso retorna o OR EXCLUSIVO lógico bit a bit de seus argumentos. Se nenhum argumento for fornecido, o resultado será zero, que é uma identidade para esta operação. (logxor ab) dará 49
Lognor Isso retorna o NOT bit a bit de seus argumentos. Se nenhum argumento for fornecido, o resultado será -1, que é uma identidade para esta operação. (lognor ab) dará -62,
logeqv Isso retorna a EQUIVALÊNCIA lógica bit a bit (também conhecida como nor exclusiva) de seus argumentos. Se nenhum argumento for fornecido, o resultado será -1, que é uma identidade para esta operação. (logeqv ab) dará -50

As estruturas de tomada de decisão requerem que o programador especifique uma ou mais condições a serem avaliadas ou testadas pelo programa, junto com uma instrução ou instruções a serem executadas se a condição for determinada como verdadeira e, opcionalmente, outras instruções a serem executadas se a condição está determinado a ser falso.

A seguir está a forma geral de uma estrutura típica de tomada de decisão encontrada na maioria das linguagens de programação -

O LISP fornece os seguintes tipos de construções de tomada de decisão. Clique nos links a seguir para verificar seus detalhes.

Sr. Não. Construir e Descrição
1 cond

Esta construção é usada para verificar várias cláusulas de ação de teste. Ele pode ser comparado às instruções if aninhadas em outras linguagens de programação.

2 E se

A construção if possui várias formas. Na forma mais simples, é seguido por uma cláusula de teste, uma ação de teste e algumas outras ações consequentes. Se a cláusula de teste for avaliada como verdadeira, a ação de teste será executada, caso contrário, a cláusula consequente será avaliada.

3 quando

Na forma mais simples, é seguido por uma cláusula de teste e uma ação de teste. Se a cláusula de teste for avaliada como verdadeira, a ação de teste será executada, caso contrário, a cláusula consequente será avaliada.

4 caso

Essa construção implementa várias cláusulas de ação de teste, como a construção cond. No entanto, ele avalia um formulário-chave e permite várias cláusulas de ação com base na avaliação desse formulário-chave.

Pode haver uma situação em que você precise executar um bloco de números de código de vezes. Uma instrução de loop nos permite executar uma instrução ou grupo de instruções várias vezes e a seguir está a forma geral de uma instrução de loop na maioria das linguagens de programação.

LISP fornece os seguintes tipos de construções para lidar com os requisitos de loop. Clique nos links a seguir para verificar seus detalhes.

Sr. Não. Construir e Descrição
1 ciclo

o loopconstruir é a forma mais simples de iteração fornecida pelo LISP. Em sua forma mais simples, permite que você execute algumas instruções repetidamente até encontrar umreturn declaração.

2 laço para

O loop for construct permite implementar um loop for como a iteração mais comum em outras linguagens.

3 Faz

A construção do também é usada para realizar iterações usando LISP. Ele fornece uma forma estruturada de iteração.

4 dotimes

A construção dotimes permite o loop para algum número fixo de iterações.

5 dolist

A construção dolist permite a iteração por meio de cada elemento de uma lista.

Saindo Graciosamente de um Bloco

o block e return-from permite que você saia normalmente de qualquer bloco aninhado no caso de qualquer erro.

o blockA função permite criar um bloco nomeado com um corpo composto de zero ou mais instruções. A sintaxe é -

(block block-name(
...
...
))

o return-from A função recebe um nome de bloco e um valor de retorno opcional (o padrão é nulo).

O exemplo a seguir demonstra isso -

Exemplo

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele -

(defun demo-function (flag)
   (print 'entering-outer-block)
   
   (block outer-block
      (print 'entering-inner-block)
      (print (block inner-block

         (if flag
            (return-from outer-block 3)
            (return-from inner-block 5)
         )

         (print 'This-wil--not-be-printed))
      )

      (print 'left-inner-block)
      (print 'leaving-outer-block)
   t)
)
(demo-function t)
(terpri)
(demo-function nil)

Ao clicar no botão Executar ou digitar Ctrl + E, o LISP o executa imediatamente e o resultado retornado é -

ENTERING-OUTER-BLOCK 
ENTERING-INNER-BLOCK 

ENTERING-OUTER-BLOCK 
ENTERING-INNER-BLOCK 
5 
LEFT-INNER-BLOCK 
LEAVING-OUTER-BLOCK

Uma função é um grupo de instruções que, juntas, executam uma tarefa.

Você pode dividir seu código em funções separadas. Como você divide seu código entre diferentes funções é com você, mas logicamente a divisão geralmente é para que cada função execute uma tarefa específica.

Definindo Funções em LISP

A macro chamada defuné usado para definir funções. odefun macro precisa de três argumentos -

  • Nome da função
  • Parâmetros da função
  • Corpo da função

A sintaxe para defun é -

(defun name (parameter-list) "Optional documentation string." body)

Vamos ilustrar o conceito com exemplos simples.

Exemplo 1

Vamos escrever uma função chamada averagenum que imprimirá a média de quatro números. Enviaremos esses números como parâmetros.

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(defun averagenum (n1 n2 n3 n4)
   (/ ( + n1 n2 n3 n4) 4)
)
(write(averagenum 10 20 30 40))

Quando você executa o código, ele retorna o seguinte resultado -

25

Exemplo 2

Vamos definir e chamar uma função que calcularia a área de um círculo quando o raio do círculo é dado como argumento.

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(defun area-circle(rad)
   "Calculates area of a circle with given radius"
   (terpri)
   (format t "Radius: ~5f" rad)
   (format t "~%Area: ~10f" (* 3.141592 rad rad))
)
(area-circle 10)

Quando você executa o código, ele retorna o seguinte resultado -

Radius:  10.0
Area:   314.1592

Observe que -

  • Você pode fornecer uma lista vazia como parâmetros, o que significa que a função não leva argumentos, a lista está vazia, escrita como ().

  • LISP também permite argumentos opcionais, múltiplos e de palavra-chave.

  • A string de documentação descreve a finalidade da função. Está associado ao nome da função e pode ser obtido usando odocumentation função.

  • O corpo da função pode consistir em qualquer número de expressões Lisp.

  • O valor da última expressão no corpo é retornado como o valor da função.

  • Você também pode retornar um valor da função usando o return-from operador especial.

Vamos discutir os conceitos acima em breve. Clique nos links a seguir para encontrar os detalhes -

  • Parâmetros Opcionais

  • Parâmetros de descanso

  • Parâmetros de palavra-chave

  • Retornando valores de uma função

  • Funções Lambda

  • Funções de mapeamento

Predicados são funções que testam seus argumentos para algumas condições específicas e retorna nulo se a condição for falsa ou se algum valor não nulo for verdadeiro.

A tabela a seguir mostra alguns dos predicados mais comumente usados ​​-

Sr. Não. Predicado e descrição
1

atom

Leva um argumento e retorna t se o argumento for um átomo ou nil se o contrário.

2

equal

Leva dois argumentos e retorna t se eles são estruturalmente iguais ou nil de outra forma.

3

eq

Leva dois argumentos e retorna t se eles são os mesmos objetos idênticos, compartilhando o mesmo local de memória ou nil de outra forma.

4

eql

Leva dois argumentos e retorna t se os argumentos são eq, ou se eles são números do mesmo tipo com o mesmo valor, ou se eles são objetos de personagem que representam o mesmo personagem, ou nil de outra forma.

5

evenp

Leva um argumento numérico e retorna t se o argumento for um número par ou nil caso contrário.

6

oddp

Leva um argumento numérico e retorna t se o argumento é um número ímpar ou nil caso contrário.

7

zerop

Leva um argumento numérico e retorna t se o argumento é zero ou nil caso contrário.

8

null

Leva um argumento e retorna t se o argumento for avaliado como nulo, caso contrário, ele retorna nil.

9

listp

Leva um argumento e retorna t se o argumento for avaliado como uma lista, caso contrário ele retorna nil.

10

greaterp

Leva um ou mais argumentos e retorna t se houver um único argumento ou se os argumentos forem sucessivamente maiores da esquerda para a direita, ou nil caso contrário.

11

lessp

Leva um ou mais argumentos e retorna t se houver um único argumento ou se os argumentos forem sucessivamente menores da esquerda para a direita, ou nil caso contrário.

12

numberp

Leva um argumento e retorna t se o argumento é um número ou nil caso contrário.

13

symbolp

Leva um argumento e retorna t se o argumento é um símbolo, caso contrário ele retorna nil.

14

integerp

Leva um argumento e retorna t se o argumento for um inteiro, caso contrário ele retorna nil.

15

rationalp

Leva um argumento e retorna t se o argumento é um número racional, uma proporção ou um número, caso contrário, ele retorna nil.

16

floatp

Leva um argumento e retorna t se o argumento é um número de ponto flutuante, caso contrário ele retorna nil.

17

realp

Leva um argumento e retorna t se o argumento é um número real, caso contrário ele retorna nil.

18

complexp

Leva um argumento e retorna t se o argumento for um número complexo, caso contrário, ele retorna nil.

19

characterp

Leva um argumento e retorna t se o argumento é um caractere, caso contrário ele retorna nil.

20

stringp

Leva um argumento e retorna t se o argumento é um objeto string, caso contrário, ele retorna nil.

21

arrayp

Leva um argumento e retorna t se o argumento for um objeto de array, caso contrário ele retorna nil.

22

packagep

Leva um argumento e retorna t se o argumento é um pacote, caso contrário ele retorna nil.

Exemplo 1

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(write (atom 'abcd))
(terpri)
(write (equal 'a 'b))
(terpri)
(write (evenp 10))
(terpri)
(write (evenp 7 ))
(terpri)
(write (oddp 7 ))
(terpri)
(write (zerop 0.0000000001))
(terpri)
(write (eq 3 3.0 ))
(terpri)
(write (equal 3 3.0 ))
(terpri)
(write (null nil ))

Quando você executa o código, ele retorna o seguinte resultado -

T
NIL
T
NIL
T
NIL
NIL
NIL
T

Exemplo 2

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(defun factorial (num)
   (cond ((zerop num) 1)
      (t ( * num (factorial (- num 1))))
   )
)
(setq n 6)
(format t "~% Factorial ~d is: ~d" n (factorial n))

Quando você executa o código, ele retorna o seguinte resultado -

Factorial 6 is: 720

Lisp comum define vários tipos de números. onumber tipo de dados inclui vários tipos de números suportados pelo LISP.

Os tipos de número suportados pelo LISP são -

  • Integers
  • Ratios
  • Números de ponto flutuante
  • Números complexos

O diagrama a seguir mostra a hierarquia numérica e vários tipos de dados numéricos disponíveis no LISP -

Vários tipos numéricos em LISP

A tabela a seguir descreve vários tipos de dados de número disponíveis no LISP -

Sr. Não. Tipo de dados e descrição
1

fixnum

Este tipo de dados representa números inteiros que não são muito grandes e principalmente na faixa de -215 a 215-1 (é dependente da máquina)

2

bignum

Esses são números muito grandes com tamanho limitado pela quantidade de memória alocada para LISP, eles não são números fixos.

3

ratio

Representa a proporção de dois números na forma de numerador / denominador. A função / sempre produz o resultado em proporções, quando seus argumentos são inteiros.

4

float

Ele representa números não inteiros. Existem quatro tipos de dados flutuantes com precisão crescente.

5

complex

Ele representa números complexos, que são denotados por #c. As partes reais e imaginárias podem ser números de ponto flutuante ou racionais.

Exemplo

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(write (/ 1 2))
(terpri)
(write ( + (/ 1 2) (/ 3 4)))
(terpri)
(write ( + #c( 1 2) #c( 3 -4)))

Quando você executa o código, ele retorna o seguinte resultado -

1/2
5/4
#C(4 -2)

Funções numéricas

A tabela a seguir descreve algumas funções numéricas comumente usadas -

Sr. Não. Descrição da função
1

+, -, *, /

Respectivas operações aritméticas

2

sin, cos, tan, acos, asin, atan

Respectivas funções trigonométricas.

3

sinh, cosh, tanh, acosh, asinh, atanh

Respectivas funções hiperbólicas.

4

exp

Função de exponenciação. Calcula e x

5

expt

Função de exponenciação, leva tanto a base quanto a potência.

6

sqrt

Ele calcula a raiz quadrada de um número.

7

log

Função logarítmica. Se um parâmetro é dado, ele calcula seu logaritmo natural, caso contrário, o segundo parâmetro é usado como base.

8

conjugate

Ele calcula o conjugado complexo de um número. No caso de um número real, ele retorna o próprio número.

9

abs

Ele retorna o valor absoluto (ou magnitude) de um número.

10

gcd

Ele calcula o maior divisor comum dos números fornecidos.

11

lcm

Ele calcula o mínimo múltiplo comum dos números fornecidos.

12

isqrt

Ele dá o maior inteiro menor ou igual à raiz quadrada exata de um determinado número natural.

13

floor, ceiling, truncate, round

Todas essas funções recebem dois argumentos como um número e retornam o quociente; floor retorna o maior inteiro que não é maior que a proporção, ceiling escolhe o número inteiro menor que é maior que a proporção, truncate escolhe o número inteiro do mesmo sinal como proporção com o maior valor absoluto que é menor do que o valor absoluto da proporção, e round escolhe um número inteiro mais próximo da proporção.

14

ffloor, fceiling, ftruncate, fround

Faz o mesmo que acima, mas retorna o quociente como um número de ponto flutuante.

15

mod, rem

Retorna o resto em uma operação de divisão.

16

float

Converte um número real em um número de ponto flutuante.

17

rational, rationalize

Converte um número real em um número racional.

18

numerator, denominator

Retorna as respectivas partes de um número racional.

19

realpart, imagpart

Retorna a parte real e imaginária de um número complexo.

Exemplo

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(write (/ 45 78))
(terpri)
(write (floor 45 78))
(terpri)
(write (/ 3456 75))
(terpri)
(write (floor 3456 75))
(terpri)
(write (ceiling 3456 75))
(terpri)
(write (truncate 3456 75))
(terpri)
(write (round 3456 75))
(terpri)
(write (ffloor 3456 75))
(terpri)
(write (fceiling 3456 75))
(terpri)
(write (ftruncate 3456 75))
(terpri)
(write (fround 3456 75))
(terpri)
(write (mod 3456 75))
(terpri)
(setq c (complex 6 7))
(write c)
(terpri)
(write (complex 5 -9))
(terpri)
(write (realpart c))
(terpri)
(write (imagpart c))

Quando você executa o código, ele retorna o seguinte resultado -

15/26
0
1152/25
46
47
46
46
46.0
47.0
46.0
46.0
6
#C(6 7)
#C(5 -9)
6
7

No LISP, os caracteres são representados como objetos de dados do tipo character.

Você pode denotar um objeto de caractere precedendo # \ antes do próprio caractere. Por exemplo, # \ a significa o caractere a.

Espaço e outros caracteres especiais podem ser indicados precedendo # \ antes do nome do caractere. Por exemplo, # \ SPACE representa o caractere de espaço.

O exemplo a seguir demonstra isso -

Exemplo

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(write 'a)
(terpri)
(write #\a)
(terpri)
(write-char #\a)
(terpri)
(write-char 'a)

Quando você executa o código, ele retorna o seguinte resultado -

A
#\a
a
*** - WRITE-CHAR: argument A is not a character

Caracteres especiais

O LISP comum permite usar os seguintes caracteres especiais em seu código. Eles são chamados de caracteres semipadrão.

  • #\Backspace
  • #\Tab
  • #\Linefeed
  • #\Page
  • #\Return
  • #\Rubout

Funções de comparação de caracteres

Funções e operadores de comparação numérica, como <e> não funcionam em caracteres. LISP comum fornece outros dois conjuntos de funções para comparar caracteres em seu código.

Um conjunto diferencia maiúsculas de minúsculas e o outro não diferencia maiúsculas de minúsculas.

A tabela a seguir fornece as funções -

Funções que diferenciam maiúsculas de minúsculas Funções que não diferenciam maiúsculas de minúsculas Descrição
char = char-equal Verifica se os valores dos operandos são todos iguais ou não, se sim então a condição torna-se verdadeira.
char / = char-not-equal Verifica se os valores dos operandos são todos diferentes ou não, se os valores não são iguais a condição torna-se verdadeira.
char < char-lessp Verifica se os valores dos operandos estão diminuindo monotonicamente.
char> char-maiorp Verifica se os valores dos operandos estão aumentando monotonicamente.
char <= char-not-higherp Verifica se o valor de algum operando à esquerda é maior ou igual ao valor do próximo operando à direita. Se sim, a condição se torna verdadeira.
char> = char-not-lessp Verifica se o valor de algum operando esquerdo é menor ou igual ao valor de seu operando direito; em caso positivo, a condição se torna verdadeira.

Exemplo

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

; case-sensitive comparison
(write (char= #\a #\b))
(terpri)
(write (char= #\a #\a))
(terpri)
(write (char= #\a #\A))
(terpri)
   
;case-insensitive comparision
(write (char-equal #\a #\A))
(terpri)
(write (char-equal #\a #\b))
(terpri)
(write (char-lessp #\a #\b #\c))
(terpri)
(write (char-greaterp #\a #\b #\c))

Quando você executa o código, ele retorna o seguinte resultado -

NIL
T
NIL
T
NIL
T
NIL

LISP permite que você defina matrizes de dimensão única ou múltipla usando o make-arrayfunção. Um array pode armazenar qualquer objeto LISP como seus elementos.

Todos os arrays consistem em locais de memória contíguos. O endereço mais baixo corresponde ao primeiro elemento e o endereço mais alto ao último elemento.

O número de dimensões de uma matriz é chamado de classificação.

No LISP, um elemento de matriz é especificado por uma sequência de índices inteiros não negativos. O comprimento da sequência deve ser igual à classificação da matriz. A indexação começa do zero.

Por exemplo, para criar um array com 10 células, chamado my-array, podemos escrever -

(setf my-array (make-array '(10)))

A função aref permite acessar o conteúdo das células. Leva dois argumentos, o nome da matriz e o valor do índice.

Por exemplo, para acessar o conteúdo da décima célula, escrevemos -

(aref my-array 9)

Exemplo 1

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(write (setf my-array (make-array '(10))))
(terpri)
(setf (aref my-array 0) 25)
(setf (aref my-array 1) 23)
(setf (aref my-array 2) 45)
(setf (aref my-array 3) 10)
(setf (aref my-array 4) 20)
(setf (aref my-array 5) 17)
(setf (aref my-array 6) 25)
(setf (aref my-array 7) 19)
(setf (aref my-array 8) 67)
(setf (aref my-array 9) 30)
(write my-array)

Quando você executa o código, ele retorna o seguinte resultado -

#(NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL)
#(25 23 45 10 20 17 25 19 67 30)

Exemplo 2

Vamos criar um array 3 por 3.

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(setf x (make-array '(3 3) 
   :initial-contents '((0 1 2 ) (3 4 5) (6 7 8)))
)
(write x)

Quando você executa o código, ele retorna o seguinte resultado -

#2A((0 1 2) (3 4 5) (6 7 8))

Exemplo 3

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(setq a (make-array '(4 3)))
(dotimes (i 4)
   (dotimes (j 3)
      (setf (aref a i j) (list i 'x j '= (* i j)))
   )
)
(dotimes (i 4)
   (dotimes (j 3)
      (print (aref a i j))
   )
)

Quando você executa o código, ele retorna o seguinte resultado -

(0 X 0 = 0) 
(0 X 1 = 0) 
(0 X 2 = 0) 
(1 X 0 = 0) 
(1 X 1 = 1) 
(1 X 2 = 2) 
(2 X 0 = 0) 
(2 X 1 = 2) 
(2 X 2 = 4) 
(3 X 0 = 0) 
(3 X 1 = 3) 
(3 X 2 = 6)

Sintaxe completa para a função make-array

A função make-array leva muitos outros argumentos. Vejamos a sintaxe completa desta função -

make-array dimensions :element-type :initial-element :initial-contents :adjustable :fill-pointer  :displaced-to :displaced-index-offset

Além do argumento de dimensões , todos os outros argumentos são palavras-chave. A tabela a seguir fornece uma breve descrição dos argumentos.

Sr. Não. Argumento e Descrição
1

dimensions

Ele fornece as dimensões da matriz. É um número para uma matriz unidimensional e uma lista para uma matriz multidimensional.

2

:element-type

É o especificador de tipo, o valor padrão é T, ou seja, qualquer tipo

3

:initial-element

Valor dos elementos iniciais. Ele fará um array com todos os elementos inicializados com um valor particular.

4

:initial-content

Conteúdo inicial como objeto.

5

:adjustable

Ajuda na criação de um vetor redimensionável (ou ajustável) cuja memória subjacente pode ser redimensionada. O argumento é um valor booleano que indica se a matriz é ajustável ou não, sendo o valor padrão NIL.

6

:fill-pointer

Ele mantém registro do número de elementos realmente armazenados em um vetor redimensionável.

7

:displaced-to

Ele ajuda a criar uma matriz deslocada ou matriz compartilhada que compartilha seu conteúdo com a matriz especificada. Ambos os arrays devem ter o mesmo tipo de elemento. A opção: displaced-to não pode ser usada com a opção: initial-element ou: initial-contents. Este argumento padrão é nulo.

8

:displaced-index-offset

Ele fornece o deslocamento de índice da matriz compartilhada criada.

Exemplo 4

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(setq myarray (make-array '(3 2 3) 
   :initial-contents 
   '(((a b c) (1 2 3)) 
      ((d e f) (4 5 6)) 
      ((g h i) (7 8 9)) 
   ))
) 
(setq array2 (make-array 4 :displaced-to myarray :displaced-index-offset 2)) 
(write myarray)
(terpri)
(write array2)

Quando você executa o código, ele retorna o seguinte resultado -

#3A(((A B C) (1 2 3)) ((D E F) (4 5 6)) ((G H I) (7 8 9)))
#(C 1 2 3)

Se a matriz deslocada for bidimensional -

(setq myarray (make-array '(3 2 3) 
   :initial-contents 
   '(((a b c) (1 2 3)) 
      ((d e f) (4 5 6)) 
      ((g h i) (7 8 9)) 
   ))
) 
(setq array2 (make-array '(3 2) :displaced-to myarray :displaced-index-offset 2)) 
(write myarray)
(terpri)
(write array2)

Quando você executa o código, ele retorna o seguinte resultado -

#3A(((A B C) (1 2 3)) ((D E F) (4 5 6)) ((G H I) (7 8 9)))
#2A((C 1) (2 3) (D E))

Vamos mudar o deslocamento do índice deslocado para 5 -

(setq myarray (make-array '(3 2 3) 
   :initial-contents 
   '(((a b c) (1 2 3)) 
      ((d e f) (4 5 6)) 
      ((g h i) (7 8 9)) 
   ))
) 
(setq array2 (make-array '(3 2) :displaced-to myarray :displaced-index-offset 5)) 
(write myarray)
(terpri)
(write array2)

Quando você executa o código, ele retorna o seguinte resultado -

#3A(((A B C) (1 2 3)) ((D E F) (4 5 6)) ((G H I) (7 8 9)))
#2A((3 D) (E F) (4 5))

Exemplo 5

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

;a one dimensional array with 5 elements, 
;initail value 5
(write (make-array 5 :initial-element 5))
(terpri)

;two dimensional array, with initial element a
(write (make-array '(2 3) :initial-element 'a))
(terpri)

;an array of capacity 14, but fill pointer 5, is 5
(write(length (make-array 14 :fill-pointer 5)))
(terpri)

;however its length is 14
(write (array-dimensions (make-array 14 :fill-pointer 5)))
(terpri)

; a bit array with all initial elements set to 1
(write(make-array 10 :element-type 'bit :initial-element 1))
(terpri)

; a character array with all initial elements set to a
; is a string actually
(write(make-array 10 :element-type 'character :initial-element #\a)) 
(terpri)

; a two dimensional array with initial values a
(setq myarray (make-array '(2 2) :initial-element 'a :adjustable t))
(write myarray)
(terpri)

;readjusting the array
(adjust-array myarray '(1 3) :initial-element 'b) 
(write myarray)

Quando você executa o código, ele retorna o seguinte resultado -

#(5 5 5 5 5)
#2A((A A A) (A A A))
5
(14)
#*1111111111
"aaaaaaaaaa"
#2A((A A) (A A))
#2A((A A B))

Strings em Common Lisp são vetores, isto é, array unidimensional de caracteres.

Literais de string são colocados entre aspas duplas. Qualquer caractere compatível com o conjunto de caracteres pode ser colocado entre aspas duplas para formar uma string, exceto o caractere de aspas duplas (") e o caractere de escape (\). No entanto, você pode incluí-los deixando-os com uma barra invertida (\).

Exemplo

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(write-line "Hello World")
(write-line "Welcome to Tutorials Point")

;escaping the double quote character
(write-line "Welcome to \"Tutorials Point\"")

Quando você executa o código, ele retorna o seguinte resultado -

Hello World
Welcome to Tutorials Point
Welcome to "Tutorials Point"

Funções de comparação de strings

Operadores e funções de comparação numérica, como <e> não funcionam em strings. LISP comum fornece outros dois conjuntos de funções para comparar strings em seu código. Um conjunto diferencia maiúsculas de minúsculas e o outro não diferencia maiúsculas de minúsculas.

A tabela a seguir fornece as funções -

Funções que diferenciam maiúsculas de minúsculas Funções que não diferenciam maiúsculas de minúsculas Descrição
string = string-igual Verifica se os valores dos operandos são todos iguais ou não, se sim então a condição torna-se verdadeira.
string / = string-not-equal Verifica se os valores dos operandos são todos diferentes ou não, se os valores não são iguais a condição torna-se verdadeira.
string < string-lessp Verifica se os valores dos operandos estão diminuindo monotonicamente.
string> string-maior Verifica se os valores dos operandos estão aumentando monotonicamente.
string <= string-not-higherp Verifica se o valor de algum operando à esquerda é maior ou igual ao valor do próximo operando à direita. Se sim, a condição se torna verdadeira.
string> = string-not-lessp Verifica se o valor de algum operando esquerdo é menor ou igual ao valor de seu operando direito; em caso positivo, a condição se torna verdadeira.

Exemplo

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

; case-sensitive comparison
(write (string= "this is test" "This is test"))
(terpri)
(write (string> "this is test" "This is test"))
(terpri)
(write (string< "this is test" "This is test"))
(terpri)

;case-insensitive comparision
(write (string-equal "this is test" "This is test"))
(terpri)
(write (string-greaterp "this is test" "This is test"))
(terpri)
(write (string-lessp "this is test" "This is test"))
(terpri)

;checking non-equal
(write (string/= "this is test" "this is Test"))
(terpri)
(write (string-not-equal "this is test" "This is test"))
(terpri)
(write (string/= "lisp" "lisping"))
(terpri)
(write (string/= "decent" "decency"))

Quando você executa o código, ele retorna o seguinte resultado -

NIL
0
NIL
T
NIL
NIL
8
NIL
4
5

Funções de controle de caso

A tabela a seguir descreve as funções de controle de caso -

Sr. Não. Descrição da função
1

string-upcase

Converte a string em maiúsculas

2

string-downcase

Converte a string em minúsculas

3

string-capitalize

Capitaliza cada palavra na string

Exemplo

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(write-line (string-upcase "a big hello from tutorials point"))
(write-line (string-capitalize "a big hello from tutorials point"))

Quando você executa o código, ele retorna o seguinte resultado -

A BIG HELLO FROM TUTORIALS POINT
A Big Hello From Tutorials Point

Corte de cordas

A tabela a seguir descreve as funções de corte de string -

Sr. Não. Descrição da função
1

string-trim

Ele pega uma string de caractere (s) como primeiro argumento e uma string como o segundo argumento e retorna uma substring onde todos os caracteres que estão no primeiro argumento são removidos da string de argumento.

2

String-left-trim

Ele pega uma string de caractere (s) como primeiro argumento e uma string como o segundo argumento e retorna uma substring onde todos os caracteres que estão no primeiro argumento são removidos do início da string do argumento.

3

String-right-trim

Ele pega um caractere (s) de string como primeiro argumento e uma string como o segundo argumento e retorna uma substring onde todos os caracteres que estão no primeiro argumento são removidos do final da string de argumento.

Exemplo

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(write-line (string-trim " " "   a big hello from tutorials point   "))
(write-line (string-left-trim " " "   a big hello from tutorials point   "))
(write-line (string-right-trim " " "   a big hello from tutorials point   "))
(write-line (string-trim " a" "   a big hello from tutorials point   "))

Quando você executa o código, ele retorna o seguinte resultado -

a big hello from tutorials point
a big hello from tutorials point   
   a big hello from tutorials point
big hello from tutorials point

Outras funções de string

Strings em LISP são arrays e, portanto, também sequências. Abordaremos esses tipos de dados nos próximos tutoriais. Todas as funções aplicáveis ​​a arrays e sequências também se aplicam a strings. No entanto, demonstraremos algumas funções comumente usadas usando vários exemplos.

Calculando Comprimento

o length função calcula o comprimento de uma string.

Extraindo Sub-string

o subseq função retorna uma substring (como string também é uma sequência) começando em um índice particular e continuando para um índice final particular ou o final da string.

Acessando um caractere em uma string

o char função permite acessar caracteres individuais de uma string.

Example

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(write (length "Hello World"))
(terpri)
(write-line (subseq "Hello World" 6))
(write (char "Hello World" 6))

Quando você executa o código, ele retorna o seguinte resultado -

11
World
#\W

Classificando e Mesclando Strings

o sortfunção permite classificar uma string. Ele pega uma sequência (vetor ou string) e um predicado de dois argumentos e retorna uma versão classificada da sequência.

o merge A função recebe duas sequências e um predicado e retorna uma sequência produzida pela fusão das duas sequências, de acordo com o predicado.

Example

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

;sorting the strings
(write (sort (vector "Amal" "Akbar" "Anthony") #'string<))
(terpri)

;merging the strings
(write (merge 'vector (vector "Rishi" "Zara" "Priyanka") 
   (vector "Anju" "Anuj" "Avni") #'string<))

Quando você executa o código, ele retorna o seguinte resultado -

#("Akbar" "Amal" "Anthony")
#("Anju" "Anuj" "Avni" "Rishi" "Zara" "Priyanka")

Invertendo uma corda

o reverse função inverte uma string.

Por exemplo, crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(write-line (reverse "Are we not drawn onward, we few, drawn onward to new era"))

Quando você executa o código, ele retorna o seguinte resultado -

are wen ot drawno nward ,wef ew ,drawno nward ton ew erA

Strings de concatenação

A função concatenate concatena duas strings. Esta é uma função de sequência genérica e você deve fornecer o tipo de resultado como o primeiro argumento.

Por exemplo, crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(write-line (concatenate 'string "Are we not drawn onward, " "we few, drawn onward to new era"))

Quando você executa o código, ele retorna o seguinte resultado -

Are we not drawn onward, we few, drawn onward to new era

Sequência é um tipo de dados abstrato em LISP. Vetores e listas são os dois subtipos concretos desse tipo de dados. Todas as funcionalidades definidas no tipo de dados de sequência são aplicadas em todos os vetores e tipos de lista.

Nesta seção, discutiremos as funções mais comumente usadas em sequências.

Antes de iniciar as várias maneiras de manipular sequências (ou seja, vetores e listas), vamos dar uma olhada na lista de todas as funções disponíveis.

Criando uma sequência

A função make-sequence permite criar uma sequência de qualquer tipo. A sintaxe para esta função é -

make-sequence sqtype sqsize &key :initial-element

Ele cria uma sequência do tipo sqtype e do comprimento sqsize.

Você pode opcionalmente especificar algum valor usando o argumento : initial-element , então cada um dos elementos será inicializado com este valor.

Por exemplo, crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(write (make-sequence '(vector float) 
   10 
   :initial-element 1.0))

Quando você executa o código, ele retorna o seguinte resultado -

#(1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0)

Funções genéricas em sequências

Sr. Não. Descrição da função
1

elt

Ele permite o acesso a elementos individuais por meio de um índice inteiro.

2

length

Ele retorna o comprimento de uma sequência.

3

subseq

Ele retorna uma subseqüência extraindo a subsequência começando em um índice específico e continuando até um índice final específico ou o final da seqüência.

4

copy-seq

Ele retorna uma sequência que contém os mesmos elementos de seu argumento.

5

fill

É usado para definir vários elementos de uma sequência com um único valor.

6

replace

Leva duas sequências e a primeira sequência de argumentos é modificada destrutivamente pela cópia de elementos sucessivos da segunda sequência de argumentos.

7

count

Ele pega um item e uma sequência e retorna o número de vezes que o item aparece na sequência.

8

reverse

Ele retorna uma sequência que contém os mesmos elementos do argumento, mas na ordem inversa.

9

nreverse

Ele retorna a mesma sequência contendo os mesmos elementos da sequência, mas na ordem inversa.

10

concatenate

Ele cria uma nova sequência contendo a concatenação de qualquer número de sequências.

11

position

Ele pega um item e uma sequência e retorna o índice do item na sequência ou nulo.

12

find

Leva um item e uma sequência. Ele encontra o item na sequência e o retorna; se não for encontrado, ele retorna nulo.

13

sort

Ele pega uma sequência e um predicado de dois argumentos e retorna uma versão classificada da sequência.

14

merge

Ele pega duas sequências e um predicado e retorna uma sequência produzida pela fusão das duas sequências, de acordo com o predicado.

15

map

Ele recebe uma função de n argumentos en sequências e retorna uma nova sequência contendo o resultado da aplicação da função aos elementos subsequentes das sequências.

16

some

Ele pega um predicado como um argumento e itera sobre a sequência do argumento e retorna o primeiro valor não NIL retornado pelo predicado ou retorna falso se o predicado nunca for satisfeito.

17

every

Ele pega um predicado como um argumento e itera sobre a sequência do argumento, ele termina, retornando falso, assim que o predicado falha. Se o predicado for sempre satisfeito, ele retorna verdadeiro.

18

notany

Ele pega um predicado como um argumento e itera sobre a sequência de argumentos, e retorna falso assim que o predicado for satisfeito ou verdadeiro se nunca for.

19

notevery

Ele pega um predicado como um argumento e itera sobre a sequência de argumentos, e retorna verdadeiro assim que o predicado falha ou falso se o predicado é sempre satisfeito.

20

reduce

Ele mapeia em uma única sequência, aplicando uma função de dois argumentos primeiro aos dois primeiros elementos da sequência e, em seguida, ao valor retornado pela função e aos elementos subsequentes da sequência.

21

search

Ele pesquisa uma sequência para localizar um ou mais elementos que satisfaçam algum teste.

22

remove

Ele pega um item e uma sequência e retorna a sequência com instâncias do item removido.

23

delete

Isso também pega um item e uma sequência e retorna uma sequência do mesmo tipo que a sequência de argumentos que possui os mesmos elementos, exceto o item.

24

substitute

Ele pega um novo item, um item existente e uma sequência e retorna uma sequência com instâncias do item existente substituídas pelo novo item.

25

nsubstitute

Ele pega um novo item, um item existente e uma sequência e retorna a mesma sequência com instâncias do item existente substituídas pelo novo item.

26

mismatch

Leva duas sequências e retorna o índice do primeiro par de elementos incompatíveis.

Argumentos de palavra-chave da função de sequência padrão

Argumento Significado Valor padrão
:teste É uma função de dois argumentos usada para comparar o item (ou valor extraído por: função da tecla) com o elemento. EQL
:chave Função de um argumento para extrair o valor da chave do elemento de sequência real. NIL significa usar o elemento como está. NADA
:começar Índice inicial (inclusive) de subsequência. 0
:fim Índice final (exclusivo) da subsequência. NIL indica o fim da sequência. NADA
: do fim Se verdadeiro, a sequência será percorrida na ordem inversa, do fim ao início. NADA
:contagem Número que indica o número de elementos a serem removidos ou substituídos ou NIL para indicar todos (REMOVER e SUBSTITUIR apenas). NADA

Acabamos de discutir várias funções e palavras-chave que são usadas como argumentos nessas funções que trabalham em sequências. Nas próximas seções, veremos como usar essas funções usando exemplos.

Encontrando Comprimento e Elemento

o length função retorna o comprimento de uma sequência, e o elt A função permite que você acesse elementos individuais usando um índice inteiro.

Exemplo

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(setq x (vector 'a 'b 'c 'd 'e))
(write (length x))
(terpri)
(write (elt x 3))

Quando você executa o código, ele retorna o seguinte resultado -

5
D

Modificando Sequências

Algumas funções de sequência permitem iterar através da sequência e realizar algumas operações, como pesquisar, remover, contar ou filtrar elementos específicos sem escrever loops explícitos.

O exemplo a seguir demonstra isso -

Exemplo 1

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(write (count 7 '(1 5 6 7 8 9 2 7 3 4 5)))
(terpri)
(write (remove 5 '(1 5 6 7 8 9 2 7 3 4 5)))
(terpri)
(write (delete 5 '(1 5 6 7 8 9 2 7 3 4 5)))
(terpri)
(write (substitute 10 7 '(1 5 6 7 8 9 2 7 3 4 5)))
(terpri)
(write (find 7 '(1 5 6 7 8 9 2 7 3 4 5)))
(terpri)
(write (position 5 '(1 5 6 7 8 9 2 7 3 4 5)))

Quando você executa o código, ele retorna o seguinte resultado -

2
(1 6 7 8 9 2 7 3 4)
(1 6 7 8 9 2 7 3 4)
(1 5 6 10 8 9 2 10 3 4 5)
7
1

Exemplo 2

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(write (delete-if #'oddp '(1 5 6 7 8 9 2 7 3 4 5)))
(terpri)
(write (delete-if #'evenp '(1 5 6 7 8 9 2 7 3 4 5)))
(terpri)
(write (remove-if #'evenp '(1 5 6 7 8 9 2 7 3 4 5) :count 1 :from-end t))
(terpri)
(setq x (vector 'a 'b 'c 'd 'e 'f 'g))
(fill x 'p :start 1 :end 4)
(write x)

Quando você executa o código, ele retorna o seguinte resultado -

(6 8 2 4)
(1 5 7 9 7 3 5)
(1 5 6 7 8 9 2 7 3 5)
#(A P P P E F G)

Classificando e mesclando sequências

As funções de classificação pegam uma sequência e um predicado de dois argumentos e retornam uma versão classificada da sequência.

Exemplo 1

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(write (sort '(2 4 7 3 9 1 5 4 6 3 8) #'<))
(terpri)
(write (sort '(2 4 7 3 9 1 5 4 6 3 8) #'>))
(terpri)

Quando você executa o código, ele retorna o seguinte resultado -

(1 2 3 3 4 4 5 6 7 8 9)
(9 8 7 6 5 4 4 3 3 2 1)

Exemplo 2

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(write (merge 'vector #(1 3 5) #(2 4 6) #'<))
(terpri)
(write (merge 'list #(1 3 5) #(2 4 6) #'<))
(terpri)

Quando você executa o código, ele retorna o seguinte resultado -

#(1 2 3 4 5 6)
(1 2 3 4 5 6)

Predicados de sequência

As funções every, some, notany e notevery são chamadas de predicados de sequência.

Essas funções iteram em sequências e testam o predicado booleano.

Todas essas funções recebem um predicado como o primeiro argumento e os argumentos restantes são sequências.

Exemplo

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(write (every #'evenp #(2 4 6 8 10)))
(terpri)
(write (some #'evenp #(2 4 6 8 10 13 14)))
(terpri)
(write (every #'evenp #(2 4 6 8 10 13 14)))
(terpri)
(write (notany #'evenp #(2 4 6 8 10)))
(terpri)
(write (notevery #'evenp #(2 4 6 8 10 13 14)))
(terpri)

Quando você executa o código, ele retorna o seguinte resultado -

T
T
NIL
NIL
T

Mapeando Sequências

Já discutimos as funções de mapeamento. Da mesma forma omap função permite que você aplique uma função em elementos subsequentes de uma ou mais sequências.

o map function recebe uma função de n argumentos en sequências e retorna uma nova sequência após aplicar a função aos elementos subsequentes das sequências.

Exemplo

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(write (map 'vector #'* #(2 3 4 5) #(3 5 4 8)))

Quando você executa o código, ele retorna o seguinte resultado -

#(6 15 16 40)

As listas foram as mais importantes e a principal estrutura de dados compostos no LISP tradicional. O LISP comum dos dias atuais fornece outras estruturas de dados como vetor, tabela de hash, classes ou estruturas.

Listas são listas únicas com links. No LISP, as listas são construídas como uma cadeia de uma estrutura de registro simples chamadacons ligados em conjunto.

A Estrutura do Registro Contras

UMA cons é uma estrutura de registro contendo dois componentes chamados de car e a cdr.

Células contras ou contras são objetos são pares de valores criados usando a função cons.

o consA função recebe dois argumentos e retorna uma nova célula cons contendo os dois valores. Esses valores podem ser referências a qualquer tipo de objeto.

Se o segundo valor não for nulo, ou outra célula cons, os valores são impressos como um par pontilhado entre parênteses.

Os dois valores em uma célula cons são chamados de car e a cdr. o car função é usada para acessar o primeiro valor e o cdr função é usada para acessar o segundo valor.

Exemplo

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(write (cons 1 2))
(terpri)
(write (cons 'a 'b))
(terpri)
(write (cons 1 nil))
(terpri)
(write (cons 1 (cons 2 nil)))
(terpri)
(write (cons 1 (cons 2 (cons 3 nil))))
(terpri)
(write (cons 'a (cons 'b (cons 'c nil))))
(terpri)
(write ( car (cons 'a (cons 'b (cons 'c nil)))))
(terpri)
(write ( cdr (cons 'a (cons 'b (cons 'c nil)))))

Quando você executa o código, ele retorna o seguinte resultado -

(1 . 2)
(A . B)
(1)
(1 2)
(1 2 3)
(A B C)
A
(B C)

O exemplo acima mostra como as estruturas de cons podem ser usadas para criar uma única lista vinculada, por exemplo, a lista (ABC) consiste em três células cons unidas por seus cdrs .

Diagramaticamente, pode ser expresso como -

Listas no LISP

Embora as células cons possam ser usadas para criar listas, no entanto, construir uma lista de conschamadas de função não podem ser a melhor solução. olist função é mais usada para criar listas no LISP.

A função de lista pode receber qualquer número de argumentos e, como é uma função, avalia seus argumentos.

o first e restfunções fornecem o primeiro elemento e a parte restante de uma lista. Os exemplos a seguir demonstram os conceitos.

Exemplo 1

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(write (list 1 2))
(terpri)
(write (list 'a 'b))
(terpri)
(write (list 1 nil))
(terpri)
(write (list 1 2 3))
(terpri)
(write (list 'a 'b 'c))
(terpri)
(write (list 3 4 'a (car '(b . c)) (* 4 -2)))
(terpri)
(write (list (list 'a 'b) (list 'c 'd 'e)))

Quando você executa o código, ele retorna o seguinte resultado -

(1 2)
(A B)
(1 NIL)
(1 2 3)
(A B C)
(3 4 A B -8)
((A B) (C D E))

Exemplo 2

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(defun my-library (title author rating availability)
   (list :title title :author author :rating rating :availabilty availability)
)

(write (getf (my-library "Hunger Game" "Collins" 9 t) :title))

Quando você executa o código, ele retorna o seguinte resultado -

"Hunger Game"

Funções de manipulação de lista

A tabela a seguir fornece algumas funções de manipulação de lista comumente usadas.

Sr. Não. Descrição da função
1

car

Ele recebe uma lista como argumento e retorna seu primeiro elemento.

2

cdr

Ele recebe uma lista como argumento e retorna uma lista sem o primeiro elemento

3

cons

Recebe dois argumentos, um elemento e uma lista, e retorna uma lista com o elemento inserido em primeiro lugar.

4

list

Recebe qualquer número de argumentos e retorna uma lista com os argumentos como elementos membros da lista.

5

append

Ele mescla duas ou mais listas em uma.

6

last

Ele pega uma lista e retorna uma lista contendo o último elemento.

7

member

Leva dois argumentos, dos quais o segundo deve ser uma lista, se o primeiro argumento for membro do segundo argumento, e retorna o restante da lista começando com o primeiro argumento.

8

reverse

Ele pega uma lista e retorna uma lista com os elementos principais na ordem inversa.

Observe que todas as funções de sequência são aplicáveis ​​às listas.

Exemplo 3

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(write (car '(a b c d e f)))
(terpri)
(write (cdr '(a b c d e f)))
(terpri)
(write (cons 'a '(b c)))
(terpri)
(write (list 'a '(b c) '(e f)))
(terpri)
(write (append '(b c) '(e f) '(p q) '() '(g)))
(terpri)
(write (last '(a b c d (e f))))
(terpri)
(write (reverse '(a b c d (e f))))

Quando você executa o código, ele retorna o seguinte resultado -

A
(B C D E F)
(A B C)
(A (B C) (E F))
(B C E F P Q G)
((E F))
((E F) D C B A)

Concatenação de funções carro e cdr

o car e cdr funções e sua combinação permite extrair qualquer elemento / membro particular de uma lista.

No entanto, as sequências das funções car e cdr podem ser abreviadas concatenando a letra a para car ed para cdr dentro das letras c e r.

Por exemplo, podemos escrever cadadr para abreviar a sequência de chamadas de função - car cdr car cdr.

Assim, (cadadr '(a (cd) (efg))) retornará d

Exemplo 4

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(write (cadadr '(a (c d) (e f g))))
(terpri)
(write (caar (list (list 'a 'b) 'c)))   
(terpri)
(write (cadr (list (list 1 2) (list 3 4))))
(terpri)

Quando você executa o código, ele retorna o seguinte resultado -

D
A
(3 4)

No LISP, um símbolo é um nome que representa objetos de dados e, curiosamente, também é um objeto de dados.

O que torna os símbolos especiais é que eles têm um componente chamado property list, ou plist.

Listas de Propriedades

LISP permite que você atribua propriedades aos símbolos. Por exemplo, vamos ter um objeto 'pessoa'. Gostaríamos que esse objeto 'pessoa' tivesse propriedades como nome, sexo, altura, peso, endereço, profissão, etc. Uma propriedade é como um nome de atributo.

Uma lista de propriedades é implementada como uma lista com um número par (possivelmente zero) de elementos. Cada par de elementos da lista constitui uma entrada; o primeiro item é oindicator, e o segundo é o value.

Quando um símbolo é criado, sua lista de propriedades está inicialmente vazia. As propriedades são criadas usandoget dentro de um setf Formato.

Por exemplo, as declarações a seguir nos permitem atribuir as propriedades título, autor e editor, e respectivos valores, a um objeto denominado (símbolo) 'livro'.

Exemplo 1

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(write (setf (get 'books'title) '(Gone with the Wind)))
(terpri)
(write (setf (get 'books 'author) '(Margaret Michel)))
(terpri)
(write (setf (get 'books 'publisher) '(Warner Books)))

Quando você executa o código, ele retorna o seguinte resultado -

(GONE WITH THE WIND)
(MARGARET MICHEL)
(WARNER BOOKS)

Várias funções da lista de propriedades permitem atribuir propriedades, bem como recuperar, substituir ou remover as propriedades de um símbolo.

o getfunção retorna a lista de propriedades do símbolo para um determinado indicador. Possui a seguinte sintaxe -

get symbol indicator &optional default

o geta função procura a lista de propriedades do símbolo fornecido para o indicador especificado; se encontrado, ela retorna o valor correspondente; caso contrário, o padrão é retornado (ou nulo, se um valor padrão não for especificado).

Exemplo 2

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(setf (get 'books 'title) '(Gone with the Wind))
(setf (get 'books 'author) '(Margaret Micheal))
(setf (get 'books 'publisher) '(Warner Books))

(write (get 'books 'title))
(terpri)
(write (get 'books 'author))
(terpri)
(write (get 'books 'publisher))

Quando você executa o código, ele retorna o seguinte resultado -

(GONE WITH THE WIND)
(MARGARET MICHEAL)
(WARNER BOOKS)

o symbol-plist função permite que você veja todas as propriedades de um símbolo.

Exemplo 3

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(setf (get 'annie 'age) 43)
(setf (get 'annie 'job) 'accountant)
(setf (get 'annie 'sex) 'female)
(setf (get 'annie 'children) 3)

(terpri)
(write (symbol-plist 'annie))

Quando você executa o código, ele retorna o seguinte resultado -

(CHILDREN 3 SEX FEMALE JOB ACCOUNTANT AGE 43)

o remprop função remove a propriedade especificada de um símbolo.

Exemplo 4

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(setf (get 'annie 'age) 43)
(setf (get 'annie 'job) 'accountant)
(setf (get 'annie 'sex) 'female)
(setf (get 'annie 'children) 3)

(terpri)
(write (symbol-plist 'annie))
(remprop 'annie 'age)
(terpri)
(write (symbol-plist 'annie))

Quando você executa o código, ele retorna o seguinte resultado -

(CHILDREN 3 SEX FEMALE JOB ACCOUNTANT AGE 43)
(CHILDREN 3 SEX FEMALE JOB ACCOUNTANT)

Vetores são matrizes unidimensionais, portanto, um subtipo de matriz. Vetores e listas são chamados coletivamente de sequências. Portanto, todas as funções genéricas de sequência e funções de matriz que discutimos até agora funcionam em vetores.

Criação de vetores

A função vetorial permite que você crie vetores de tamanho fixo com valores específicos. Leva qualquer número de argumentos e retorna um vetor contendo esses argumentos.

Exemplo 1

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(setf v1 (vector 1 2 3 4 5))
(setf v2 #(a b c d e))
(setf v3 (vector 'p 'q 'r 's 't))

(write v1)
(terpri)
(write v2)
(terpri)
(write v3)

Quando você executa o código, ele retorna o seguinte resultado -

#(1 2 3 4 5)
#(A B C D E)
#(P Q R S T)

Observe que o LISP usa a sintaxe # (...) como a notação literal para vetores. Você pode usar esta sintaxe # (...) para criar e incluir vetores literais em seu código.

No entanto, esses são vetores literais, portanto, modificá-los não é definido no LISP. Portanto, para programação, você deve sempre usar ovector função, ou a função mais geral make-array para criar vetores que você planeja modificar.

o make-arrayfunção é a forma mais genérica de criar um vetor. Você pode acessar os elementos do vetor usando oaref função.

Exemplo 2

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(setq a (make-array 5 :initial-element 0))
(setq b (make-array 5 :initial-element 2))

(dotimes (i 5)
   (setf (aref a i) i))
   
(write a)
(terpri)
(write b)
(terpri)

Quando você executa o código, ele retorna o seguinte resultado -

#(0 1 2 3 4)
#(2 2 2 2 2)

Ponteiro de preenchimento

o make-array A função permite que você crie um vetor redimensionável.

o fill-pointerO argumento da função rastreia o número de elementos realmente armazenados no vetor. É o índice da próxima posição a ser preenchida quando você adiciona um elemento ao vetor.

o vector-pushA função permite adicionar um elemento ao final de um vetor redimensionável. Ele aumenta o ponteiro de preenchimento em 1.

o vector-pop função retorna o item empurrado mais recentemente e diminui o ponteiro de preenchimento em 1.

Exemplo

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(setq a (make-array 5 :fill-pointer 0))
(write a)

(vector-push 'a a)
(vector-push 'b a)
(vector-push 'c a)

(terpri)
(write a)
(terpri)

(vector-push 'd a)
(vector-push 'e a)

;this will not be entered as the vector limit is 5
(vector-push 'f a)

(write a)
(terpri)

(vector-pop a)
(vector-pop a)
(vector-pop a)

(write a)

Quando você executa o código, ele retorna o seguinte resultado -

#()
#(A B C)
#(A B C D E)
#(A B)

Vetores sendo sequências, todas as funções de sequência são aplicáveis ​​a vetores. Consulte o capítulo de sequências, para funções vetoriais.

O Common Lisp não fornece um tipo de dado definido. No entanto, ele fornece várias funções que permitem que as operações definidas sejam realizadas em uma lista.

Você pode adicionar, remover e pesquisar itens em uma lista, com base em vários critérios. Você também pode realizar várias operações de conjunto como: união, interseção e diferença de conjunto.

Implementando Conjuntos em LISP

Conjuntos, como listas, geralmente são implementados em termos de células cons. No entanto, por isso mesmo, as operações de conjuntos tornam-se cada vez menos eficientes quanto maiores se tornam os conjuntos.

o adjoinfunção permite que você construa um conjunto. Ele pega um item e uma lista que representa um conjunto e retorna uma lista que representa o conjunto que contém o item e todos os itens do conjunto original.

o adjoina função primeiro procura o item na lista fornecida; se for encontrado, ela retorna a lista original; caso contrário, ele cria uma nova célula cons com seucar como o item e cdr apontando para a lista original e retorna esta nova lista.

o adjoin função também leva :key e :testargumentos de palavra-chave. Esses argumentos são usados ​​para verificar se o item está presente na lista original.

Como a função adjoin não modifica a lista original, para fazer uma mudança na própria lista, você deve atribuir o valor retornado por adjoin à lista original ou pode usar a macro pushnew para adicionar um item ao conjunto.

Exemplo

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

; creating myset as an empty list
(defparameter *myset* ())
(adjoin 1 *myset*)
(adjoin 2 *myset*)

; adjoin did not change the original set
;so it remains same
(write *myset*)
(terpri)
(setf *myset* (adjoin 1 *myset*))
(setf *myset* (adjoin 2 *myset*))

;now the original set is changed
(write *myset*)
(terpri)

;adding an existing value
(pushnew 2 *myset*)

;no duplicate allowed
(write *myset*)
(terpri)

;pushing a new value
(pushnew 3 *myset*)
(write *myset*)
(terpri)

Quando você executa o código, ele retorna o seguinte resultado -

NIL
(2 1)
(2 1)
(3 2 1)

Verificando membros

O grupo de funções membro permite que você verifique se um elemento é membro de um conjunto ou não.

A seguir estão as sintaxes dessas funções -

member item list &key :test :test-not :key 
member-if predicate list &key :key 
member-if-not predicate list &key :key

Essas funções procuram na lista dada um determinado item que satisfaça o teste. Se nenhum item for encontrado, a função retornanil. Caso contrário, o final da lista com o elemento como o primeiro elemento é retornado.

A pesquisa é conduzida apenas no nível superior.

Essas funções podem ser usadas como predicados.

Exemplo

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(write (member 'zara '(ayan abdul zara riyan nuha)))
(terpri)
(write (member-if #'evenp '(3 7 2 5/3 'a)))
(terpri)
(write (member-if-not #'numberp '(3 7 2 5/3 'a 'b 'c)))

Quando você executa o código, ele retorna o seguinte resultado -

(ZARA RIYAN NUHA)
(2 5/3 'A)
('A 'B 'C)

Definir União

O grupo de funções de união permite que você execute a união de conjuntos em duas listas fornecidas como argumentos para essas funções com base em um teste.

A seguir estão as sintaxes dessas funções -

union list1 list2 &key :test :test-not :key 
nunion list1 list2 &key :test :test-not :key

o unionA função recebe duas listas e retorna uma nova lista contendo todos os elementos presentes em qualquer uma das listas. Se houver duplicações, apenas uma cópia do membro será retida na lista devolvida.

o nunion A função executa a mesma operação, mas pode destruir as listas de argumentos.

Exemplo

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(setq set1 (union '(a b c) '(c d e)))
(setq set2 (union '(#(a b) #(5 6 7) #(f h)) 
   '(#(5 6 7) #(a b) #(g h)) :test-not #'mismatch)
)
       
(setq set3 (union '(#(a b) #(5 6 7) #(f h)) 
   '(#(5 6 7) #(a b) #(g h)))
)
(write set1)
(terpri)
(write set2)
(terpri)
(write set3)

Quando você executa o código, ele retorna o seguinte resultado -

(A B C D E)
(#(F H) #(5 6 7) #(A B) #(G H))
(#(A B) #(5 6 7) #(F H) #(5 6 7) #(A B) #(G H))

Observe

A função de união não funciona como esperado sem :test-not #'mismatchargumentos para uma lista de três vetores. Isso ocorre porque, as listas são feitas de células cons e, embora os valores pareçam iguais para nós, aparentemente, ocdrparte das células não corresponde, portanto, não são exatamente iguais ao interpretador / compilador LISP. Esta é a razão; implementar grandes conjuntos não é aconselhável usar listas. Ele funciona bem para pequenos conjuntos.

Definir interseção

O grupo de interseção de funções permite que você execute a interseção em duas listas fornecidas como argumentos para essas funções com base em um teste.

A seguir estão as sintaxes dessas funções -

intersection list1 list2 &key :test :test-not :key 
nintersection list1 list2 &key :test :test-not :key

Essas funções recebem duas listas e retornam uma nova lista contendo todos os elementos presentes em ambas as listas de argumentos. Se qualquer uma das listas tiver entradas duplicadas, as entradas redundantes podem ou não aparecer no resultado.

Exemplo

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(setq set1 (intersection '(a b c) '(c d e)))
(setq set2 (intersection '(#(a b) #(5 6 7) #(f h)) 
   '(#(5 6 7) #(a b) #(g h)) :test-not #'mismatch)
)
       
(setq set3 (intersection '(#(a b) #(5 6 7) #(f h)) 
   '(#(5 6 7) #(a b) #(g h)))
)
(write set1)
(terpri)
(write set2)
(terpri)
(write set3)

Quando você executa o código, ele retorna o seguinte resultado -

(C)
(#(A B) #(5 6 7))
NIL

A função de interseção é a versão destrutiva da interseção, ou seja, ela pode destruir as listas originais.

Definir diferença

O grupo de funções de diferença de conjuntos permite que você execute diferenças de conjuntos em duas listas fornecidas como argumentos para essas funções com base em um teste.

A seguir estão as sintaxes dessas funções -

set-difference list1 list2 &key :test :test-not :key 
nset-difference list1 list2 &key :test :test-not :key

A função de diferença de conjunto retorna uma lista de elementos da primeira lista que não aparecem na segunda lista.

Exemplo

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(setq set1 (set-difference '(a b c) '(c d e)))
(setq set2 (set-difference '(#(a b) #(5 6 7) #(f h)) 
   '(#(5 6 7) #(a b) #(g h)) :test-not #'mismatch)
)
(setq set3 (set-difference '(#(a b) #(5 6 7) #(f h)) 
   '(#(5 6 7) #(a b) #(g h)))
)
(write set1)
(terpri)
(write set2)
(terpri)
(write set3)

Quando você executa o código, ele retorna o seguinte resultado -

(A B)
(#(F H))
(#(A B) #(5 6 7) #(F H))

Você pode construir estruturas de dados em árvore a partir de células cons, como listas de listas.

Para implementar estruturas de árvore, você terá que projetar funcionalidades que atravessariam as células cons, em ordem específica, por exemplo, pré-ordem, ordem e pós-ordem para árvores binárias.

Árvore como lista de listas

Vamos considerar uma estrutura em árvore composta por células cons que formam a seguinte lista de listas -

((1 2) (3 4) (5 6)).

Diagramaticamente, pode ser expresso como -

Funções de árvore em LISP

Embora na maioria das vezes você precise escrever suas próprias funcionalidades de árvore de acordo com sua necessidade específica, o LISP fornece algumas funções de árvore que você pode usar.

Além de todas as funções de lista, as seguintes funções funcionam especialmente em estruturas de árvore -

Sr. Não. Descrição da função
1

copy-tree x e vecp opcional

Ele retorna uma cópia da árvore de células cons x. Ele copia recursivamente as direções do carro e do cdr. Se x não for uma célula cons, a função simplesmente retorna x inalterado. Se o argumento opcional vecp for verdadeiro, esta função copia vetores (recursivamente), bem como células cons.

2

tree-equal xy & key: test: test-not: key

Ele compara duas árvores de células cons. Se xey são células cons, seus carros e cdrs são comparados recursivamente. Se nem x nem y forem células cons, são comparados pelo eql ou de acordo com o teste especificado. A função: key, se especificada, é aplicada aos elementos de ambas as árvores.

3

subst nova árvore antiga e chave: teste: não teste: chave

Substitui as ocorrências de um determinado item antigo por um novo item, na árvore , que é uma árvore de células cons.

4

nsubst nova árvore antiga e chave: teste: não teste: chave

Funciona da mesma forma que subst, mas destrói a árvore original.

5

sublis árvore de lista e chave: teste: não teste: chave

Ele funciona como subst, exceto que ele leva uma lista de associação alist de pares novo old-. Cada elemento da árvore (após aplicar a função: key, se houver), é comparado com os carros de alist; se for igual, ele será substituído pelo cdr correspondente.

6

nsublis árvore de lista e chave: teste: não teste: chave

Funciona como sublis, mas é uma versão destrutiva.

Exemplo 1

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(setq lst (list '(1 2) '(3 4) '(5 6)))
(setq mylst (copy-list lst))
(setq tr (copy-tree lst))

(write lst)
(terpri)
(write mylst)
(terpri)
(write tr)

Quando você executa o código, ele retorna o seguinte resultado -

((1 2) (3 4) (5 6))
((1 2) (3 4) (5 6))
((1 2) (3 4) (5 6))

Exemplo 2

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(setq tr '((1 2 (3 4 5) ((7 8) (7 8 9)))))
(write tr)
(setq trs (subst 7 1 tr))
(terpri)
(write trs)

Quando você executa o código, ele retorna o seguinte resultado -

((1 2 (3 4 5) ((7 8) (7 8 9))))
((7 2 (3 4 5) ((7 8) (7 8 9))))

Construindo sua própria árvore

Vamos tentar construir nossa própria árvore, usando as funções de lista disponíveis no LISP.

Primeiro, vamos criar um novo nó que contém alguns dados

(defun make-tree (item)
   "it creates a new node with item."
   (cons (cons item nil) nil)
)

A seguir, vamos adicionar um nó filho à árvore - serão necessários dois nós da árvore e adicionar a segunda árvore como filho do primeiro.

(defun add-child (tree child)
   (setf (car tree) (append (car tree) child))
   tree)

Esta função irá retornar o primeiro filho de uma determinada árvore - ela pegará um nó da árvore e retornará o primeiro filho desse nó, ou nil, se este nó não tiver nenhum nó filho.

(defun first-child (tree)
   (if (null tree)
      nil
      (cdr (car tree))
   )
)

Esta função irá retornar o próximo irmão de um determinado nó - ela pega um nó da árvore como argumento e retorna uma referência ao próximo nó irmão, ou nil, se o nó não tiver nenhum.

(defun next-sibling (tree)
   (cdr tree)
)

Por último, precisamos de uma função para retornar as informações em um nó -

(defun data (tree)
   (car (car tree))
)

Exemplo

Este exemplo usa as funcionalidades acima -

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(defun make-tree (item)
   "it creates a new node with item."
   (cons (cons item nil) nil)
)
(defun first-child (tree)
   (if (null tree)
      nil
      (cdr (car tree))
   )
)

(defun next-sibling (tree)
   (cdr tree)
)
(defun data (tree)
   (car (car tree))
)
(defun add-child (tree child)
   (setf (car tree) (append (car tree) child))
   tree
)

(setq tr '((1 2 (3 4 5) ((7 8) (7 8 9)))))
(setq mytree (make-tree 10))

(write (data mytree))
(terpri)
(write (first-child tr))
(terpri)
(setq newtree (add-child tr mytree))
(terpri)
(write newtree)

Quando você executa o código, ele retorna o seguinte resultado -

10
(2 (3 4 5) ((7 8) (7 8 9)))

((1 2 (3 4 5) ((7 8) (7 8 9)) (10)))

A estrutura de dados da tabela hash representa uma coleção de key-and-valuepares que são organizados com base no código hash da chave. Ele usa a chave para acessar os elementos da coleção.

Uma tabela hash é usada quando você precisa acessar elementos usando uma chave e pode identificar um valor de chave útil. Cada item da tabela hash possui um par chave / valor. A chave é usada para acessar os itens da coleção.

Criando Hash Table em LISP

No Common LISP, a tabela hash é uma coleção de propósito geral. Você pode usar objetos arbitrários como uma chave ou índices.

Quando você armazena um valor em uma tabela hash, você cria um par de valores-chave e o armazena sob essa chave. Posteriormente, você pode recuperar o valor da tabela de hash usando a mesma chave. Cada chave mapeia para um único valor, embora você possa armazenar um novo valor em uma chave.

As tabelas de hash, em LISP, podem ser categorizadas em três tipos, com base na forma como as chaves podem ser comparadas - eq, eql ou igual. Se a tabela hash tiver hash em objetos LISP, as chaves serão comparadas com eq ou eql. Se a tabela hash hash na estrutura de árvore, então ela seria comparada usando igual.

o make-hash-tablefunção é usada para criar uma tabela hash. A sintaxe para esta função é -

make-hash-table &key :test :size :rehash-size :rehash-threshold

Onde -

  • o key argumento fornece a chave.

  • o :testargumento determina como as chaves são comparadas - deve ter um dos três valores # 'eq, #' eql ou # 'igual, ou um dos três símbolos eq, eql ou igual. Se não for especificado, eql será assumido.

  • o :sizeargumento define o tamanho inicial da tabela hash. Deve ser um número inteiro maior que zero.

  • o :rehash-sizeargumento especifica quanto aumentar o tamanho da tabela hash quando ela ficar cheia. Pode ser um número inteiro maior que zero, que é o número de entradas a serem adicionadas, ou pode ser um número de ponto flutuante maior que 1, que é a proporção do novo tamanho em relação ao antigo. O valor padrão para este argumento depende da implementação.

  • o :rehash-thresholdargumento especifica o quão cheia a tabela hash pode ficar antes de crescer. Pode ser um número inteiro maior que zero e menor que: rehash-size (nesse caso, ele será escalado sempre que a tabela aumentar) ou pode ser um número de ponto flutuante entre zero e 1. O valor padrão para isso argumento é dependente da implementação.

Você também pode chamar a função make-hash-table sem argumentos.

Recuperando itens e adicionando itens à tabela de hash

o gethashfunção recupera um item da tabela hash procurando por sua chave. Se não encontrar a chave, retornará nil.

Possui a seguinte sintaxe -

gethash key hash-table &optional default

onde -

  • chave: é a chave associada

  • tabela de hash: é a tabela de hash a ser pesquisada

  • default: é o valor a ser retornado, se a entrada não for encontrada, que é nulo, se não for especificado.

o gethash na verdade, a função retorna dois valores, sendo o segundo um valor de predicado verdadeiro se uma entrada for encontrada e falso se nenhuma entrada for encontrada.

Para adicionar um item à tabela hash, você pode usar o setf funcionar junto com o gethash função.

Exemplo

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(setq empList (make-hash-table)) 
(setf (gethash '001 empList) '(Charlie Brown))
(setf (gethash '002 empList) '(Freddie Seal)) 
(write (gethash '001 empList)) 
(terpri)
(write (gethash '002 empList))

Quando você executa o código, ele retorna o seguinte resultado -

(CHARLIE BROWN)
(FREDDIE SEAL)

Removendo uma entrada

o remhashfunção remove qualquer entrada para uma chave específica na tabela de hash. Este é um predicado que é verdadeiro se houver uma entrada ou falso se não houver.

A sintaxe para esta função é -

remhash key hash-table

Exemplo

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(setq empList (make-hash-table)) 
(setf (gethash '001 empList) '(Charlie Brown))
(setf (gethash '002 empList) '(Freddie Seal)) 
(setf (gethash '003 empList) '(Mark Mongoose)) 

(write (gethash '001 empList)) 
(terpri)
(write (gethash '002 empList)) 
(terpri)
(write (gethash '003 empList))  
(remhash '003 empList)
(terpri)
(write (gethash '003 empList))

Quando você executa o código, ele retorna o seguinte resultado -

(CHARLIE BROWN)
(FREDDIE SEAL)
(MARK MONGOOSE)
NIL

A função maphash

o maphash função permite que você aplique uma função especificada em cada par de valores-chave em uma tabela hash.

Recebe dois argumentos - a função e uma tabela hash e invoca a função uma vez para cada par chave / valor na tabela hash.

Exemplo

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(setq empList (make-hash-table)) 
(setf (gethash '001 empList) '(Charlie Brown))
(setf (gethash '002 empList) '(Freddie Seal)) 
(setf (gethash '003 empList) '(Mark Mongoose)) 

(maphash #'(lambda (k v) (format t "~a => ~a~%" k v)) empList)

Quando você executa o código, ele retorna o seguinte resultado -

3 => (MARK MONGOOSE)
2 => (FREDDIE SEAL)
1 => (CHARLIE BROWN)

O LISP comum fornece várias funções de entrada e saída. Já usamos a função de formato e a função de impressão para saída. Nesta seção, veremos algumas das funções de entrada-saída mais comumente usadas fornecidas no LISP.

Funções de entrada

A tabela a seguir fornece as funções de entrada mais comumente usadas do LISP -

Sr. Não. Descrição da função
1

read& fluxo de entrada opcional eof-erro-p eof-valor recursivo-p

Ele lê a representação impressa de um objeto Lisp do fluxo de entrada, constrói um objeto Lisp correspondente e retorna o objeto.

2

read-preserving-whitespacee opcional in-stream eof-erro-p eof-valor recursivo-p

É usado em algumas situações especializadas em que é desejável determinar precisamente qual caractere encerrou o token estendido.

3

read-line& fluxo de entrada opcional eof-erro-p eof-valor recursivo-p

Ele lê uma linha de texto terminada por uma nova linha.

4

read-char& fluxo de entrada opcional eof-erro-p eof-valor recursivo-p

Ele pega um caractere do fluxo de entrada e o retorna como um objeto de caractere.

5

unread-char caractere e fluxo de entrada opcional

Ele coloca o caractere lido mais recentemente do fluxo de entrada na frente do fluxo de entrada.

6

peek-char& opcional peek-type input-stream eof-error-p eof-value recursive-p

Ele retorna o próximo caractere a ser lido do fluxo de entrada, sem realmente removê-lo do fluxo de entrada.

7

listen& fluxo de entrada opcional

O predicado listen é verdadeiro se houver um caractere imediatamente disponível no fluxo de entrada e é falso se não houver.

8

read-char-no-hang& fluxo de entrada opcional eof-erro-p eof-valor recursivo-p

É similar à read-char, mas se não obtiver um caractere, não espera por um caractere, mas retorna nil imediatamente.

9

clear-input& fluxo de entrada opcional

Ele limpa qualquer entrada armazenada em buffer associada ao fluxo de entrada.

10

read-from-string string & opcional eof-error-p eof-value & key: start: end: preserve-whitespace

Ele pega os caracteres da string sucessivamente e constrói um objeto LISP e retorna o objeto. Ele também retorna o índice do primeiro caractere na string não lida, ou o comprimento da string (ou, comprimento +1), conforme o caso.

11

parse-integer string & key: start: end: radix: junk-allowed

Ele examina a substring da string delimitada por: start e: end (padrão para o início e o fim da string). Ele pula os caracteres de espaço em branco e, em seguida, tenta analisar um número inteiro.

12

read-byte fluxo de entrada binária e valor eof-erro-p opcional

Ele lê um byte do fluxo de entrada binária e o retorna na forma de um inteiro.

Ler entrada do teclado

o readfunção é usada para obter dados do teclado. Pode não levar nenhum argumento.

Por exemplo, considere o snippet de código -

(write ( + 15.0 (read)))

Suponha que o usuário insira 10.2 na entrada STDIN, ele retorna,

25.2

A função read lê caracteres de um fluxo de entrada e os interpreta analisando como representações de objetos Lisp.

Exemplo

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele -

; the function AreaOfCircle
; calculates area of a circle
; when the radius is input from keyboard

(defun AreaOfCircle()
(terpri)
(princ "Enter Radius: ")
(setq radius (read))
(setq area (* 3.1416 radius radius))
(princ "Area: ")
(write area))
(AreaOfCircle)

Quando você executa o código, ele retorna o seguinte resultado -

Enter Radius: 5 (STDIN Input)
Area: 78.53999

Exemplo

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(with-input-from-string (stream "Welcome to Tutorials Point!")
   (print (read-char stream))
   (print (read-char stream))
   (print (read-char stream))
   (print (read-char stream))
   (print (read-char stream))
   (print (read-char stream))
   (print (read-char stream))
   (print (read-char stream))
   (print (read-char stream))
   (print (read-char stream))
   (print (peek-char nil stream nil 'the-end))
   (values)
)

Quando você executa o código, ele retorna o seguinte resultado -

#\W 
#\e 
#\l 
#\c 
#\o 
#\m 
#\e 
#\Space 
#\t 
#\o 
#\Space

As funções de saída

Todas as funções de saída no LISP recebem um argumento opcional chamado output-stream, onde a saída é enviada. Se não for mencionado ou nulo, o padrão do fluxo de saída é o valor da variável * saída padrão *.

A tabela a seguir fornece as funções de saída mais comumente usadas do LISP -

Sr. Não. Função e descrição
1

write objeto e chave: stream: escape: radix: base: circle: pretty: level: length: case: gensym: array

write object & key: stream: escape: radix: base: circle: pretty: level: length: case: gensym: array: readably: right-margin: miser-width: lines: pprint-dispatch

Ambos gravam o objeto no fluxo de saída especificado por: stream, cujo padrão é o valor de * saída padrão *. Outros valores são padronizados para as variáveis ​​globais correspondentes definidas para impressão.

2

prin1 objeto e fluxo de saída opcional

print objeto e fluxo de saída opcional

pprint objeto e fluxo de saída opcional

princ objeto e fluxo de saída opcional

Todas essas funções geram a representação impressa do objeto para o fluxo de saída . No entanto, as seguintes diferenças existem -

  • prin1 retorna o objeto como seu valor.

  • print imprime o objeto com uma nova linha precedente e seguida por um espaço. Ele retorna o objeto.

  • pprint é igual a print, exceto que o espaço à direita é omitido.

  • princ é como prin1 exceto que a saída não tem caractere de escape

3

write-to-string object & key : escape: radix: base: circle: pretty: level: length: case: gensym: array

write-to-string object & key: escape: radix: base: circle: pretty: level: length: case: gensym: array: readably: right-margin: miser-width: lines: pprint-dispatch

prin1-to-string objeto

princ-to-string objeto

O objeto é efetivamente impresso e os caracteres de saída são transformados em uma string, que é retornada.

4

write-char caractere e fluxo de saída opcional

Ele envia o caractere para o fluxo de saída e retorna o caractere.

5

write-string string e fluxo de saída opcional e chave: início: fim

Ele grava os caracteres da substring especificada de string no fluxo de saída.

6

write-line string e fluxo de saída opcional e chave: início: fim

Funciona da mesma maneira que a string de escrita, mas depois produz uma nova linha.

7

terpri& fluxo de saída opcional

Ele produz uma nova linha para o fluxo de saída.

8

fresh-line& fluxo de saída opcional

ele produz uma nova linha apenas se o fluxo ainda não estiver no início de uma linha.

9

finish-output& fluxo de saída opcional

force-output& fluxo de saída opcional

clear-output& fluxo de saída opcional

  • A função finish-output tenta garantir que toda a saída enviada para o fluxo de saída atingiu seu destino e só então retorna nil.

  • A função force-output inicia o esvaziamento de quaisquer buffers internos, mas retorna nulo sem esperar pela conclusão ou confirmação.

  • A função clear-output tenta abortar qualquer operação de saída pendente em andamento para permitir o mínimo de saída possível para continuar até o destino.

10

write-byte fluxo de saída binária inteiro

Ele grava um byte, o valor do inteiro.

Exemplo

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

; this program inputs a numbers and doubles it
(defun DoubleNumber()
   (terpri)
   (princ "Enter Number : ")
   (setq n1 (read))
   (setq doubled (* 2.0 n1))
   (princ "The Number: ")
   (write n1)
   (terpri)
   (princ "The Number Doubled: ")
   (write doubled)
)
(DoubleNumber)

Quando você executa o código, ele retorna o seguinte resultado -

Enter Number : 3456.78 (STDIN Input)
The Number: 3456.78
The Number Doubled: 6913.56

Saída formatada

A função formaté usado para produzir texto bem formatado. Possui a seguinte sintaxe -

format destination control-string &rest arguments

Onde,

  • destino é a saída padrão
  • string de controle contém os caracteres a serem produzidos e a diretiva de impressão.

UMA format directive consiste em um til (~), parâmetros de prefixo opcionais separados por vírgulas, dois pontos opcionais (:) e modificadores de arroba (@) e um único caractere indicando que tipo de diretiva é essa.

Os parâmetros de prefixo são geralmente inteiros, notados como números decimais opcionalmente assinados.

A tabela a seguir fornece uma breve descrição das diretivas comumente usadas -

Sr. Não. Diretriz e descrição
1

~A

É seguido por argumentos ASCII.

2

~S

É seguido por S-expressões.

3

~D

Para argumentos decimais.

4

~B

Para argumentos binários.

5

~O

Para argumentos octais.

6

~X

Para argumentos hexadecimais.

7

~C

Para argumentos de personagem.

8

~F

Para argumentos de ponto flutuante de formato fixo.

9

~E

Argumentos de ponto flutuante exponencial.

10

~$

Argumentos de dólar e ponto flutuante.

11

~%

Uma nova linha é impressa.

12

~*

O próximo argumento é ignorado.

13

~?

Indireção. O próximo argumento deve ser uma string e o seguinte uma lista.

Exemplo

Vamos reescrever o programa calculando a área de um círculo -

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(defun AreaOfCircle()
   (terpri)
   (princ "Enter Radius: ")
   (setq radius (read))
   (setq area (* 3.1416 radius radius))
   (format t "Radius: = ~F~% Area = ~F" radius area)
)
(AreaOfCircle)

Quando você executa o código, ele retorna o seguinte resultado -

Enter Radius: 10.234 (STDIN Input)
Radius: = 10.234
Area = 329.03473

Discutimos sobre como a entrada e a saída padrão são tratadas pelo LISP comum. Todas essas funções funcionam para ler e escrever em arquivos de texto e binários também. A única diferença é, neste caso, o fluxo que usamos não é uma entrada ou saída padrão, mas um fluxo criado com o propósito específico de escrever ou ler arquivos.

Neste capítulo, veremos como o LISP pode criar, abrir, fechar texto ou arquivos binários para seu armazenamento de dados.

Um arquivo representa uma seqüência de bytes, não importa se é um arquivo de texto ou binário. Este capítulo o levará através de funções / macros importantes para o gerenciamento de arquivos.

Abrindo arquivos

Você pode usar o openfunção para criar um novo arquivo ou para abrir um arquivo existente. É a função mais básica para abrir um arquivo. No entanto, owith-open-file geralmente é mais conveniente e mais comumente usado, como veremos mais adiante nesta seção.

Quando um arquivo é aberto, um objeto de fluxo é construído para representá-lo no ambiente LISP. Todas as operações no stream são basicamente equivalentes às operações no arquivo.

Sintaxe para o open função é -

open filename &key :direction :element-type :if-exists :if-does-not-exist :external-format

Onde,

  • O argumento filename é o nome do arquivo a ser aberto ou criado.

  • Os argumentos de palavra-chave especificam o tipo de fluxo e as formas de tratamento de erros.

  • o :direction palavra-chave especifica se o fluxo deve lidar com entrada, saída ou ambos, leva os seguintes valores -

    • : entrada - para fluxos de entrada (valor padrão)

    • : saída - para fluxos de saída

    • : io - para fluxos bidirecionais

    • : probe - apenas para verificar a existência de arquivos; o fluxo é aberto e depois fechado.

  • o :element-type especifica o tipo da unidade de transação para o fluxo.

  • o :if-existsO argumento especifica a ação a ser tomada se: direction for: output ou: io e um arquivo com o nome especificado já existir. Se a direção for: input ou: probe, este argumento é ignorado. Leva os seguintes valores -

    • : erro - sinaliza um erro.

    • : nova versão - cria um novo arquivo com o mesmo nome, mas com um número de versão maior.

    • : renomear - renomeia o arquivo existente.

    • : renomear e excluir - renomeia o arquivo existente e, em seguida, o exclui.

    • : anexar - anexa ao arquivo existente.

    • : substitui - substitui o arquivo existente.

    • nil - não cria um arquivo ou mesmo um fluxo apenas retorna nil para indicar falha.

  • o :if-does-not-existargumento especifica a ação a ser executada se um arquivo com o nome especificado ainda não existir. Leva os seguintes valores -

    • : erro - sinaliza um erro.

    • : create - cria um arquivo vazio com o nome especificado e o usa.

    • nil - ele não cria um arquivo ou mesmo um fluxo, mas simplesmente retorna nil para indicar falha.

  • o :external-format argumento especifica um esquema reconhecido pela implementação para representar caracteres em arquivos.

Por exemplo, você pode abrir um arquivo chamado myfile.txt armazenado na pasta / tmp como -

(open "/tmp/myfile.txt")

Gravando e lendo de arquivos

o with-open-filepermite a leitura ou gravação em um arquivo, usando a variável stream associada à transação de leitura / gravação. Assim que o trabalho for concluído, ele fecha automaticamente o arquivo. É extremamente conveniente de usar.

Possui a seguinte sintaxe -

with-open-file (stream filename {options}*)
   {declaration}* {form}*
  • filename é o nome do arquivo a ser aberto; pode ser uma string, um nome de caminho ou um fluxo.

  • As opções são iguais aos argumentos de palavra-chave para a função aberta.

Exemplo 1

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(with-open-file (stream "/tmp/myfile.txt" :direction :output)
   (format stream "Welcome to Tutorials Point!")
   (terpri stream)
   (format stream "This is a tutorials database")
   (terpri stream)
   (format stream "Submit your Tutorials, White Papers and Articles into our Tutorials   Directory.")
)

Observe que todas as funções de entrada-saída discutidas no capítulo anterior, como terpri e formato estão funcionando para gravar no arquivo que criamos aqui.

Quando você executa o código, ele não retorna nada; entretanto, nossos dados são gravados no arquivo. o:direction :output palavras-chave nos permitem fazer isso.

No entanto, podemos ler este arquivo usando o read-line função.

Exemplo 2

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(let ((in (open "/tmp/myfile.txt" :if-does-not-exist nil)))
   (when in
      (loop for line = (read-line in nil)
      
      while line do (format t "~a~%" line))
      (close in)
   )
)

Quando você executa o código, ele retorna o seguinte resultado -

Welcome to Tutorials Point!
This is a tutorials database
Submit your Tutorials, White Papers and Articles into our Tutorials Directory.

Fechando Arquivo

o close função fecha um fluxo.

Estruturas são um tipo de dados definidos pelo usuário, o que permite combinar itens de dados de diferentes tipos.

As estruturas são usadas para representar um registro. Suponha que você queira manter o controle de seus livros em uma biblioteca. Você pode querer rastrear os seguintes atributos sobre cada livro -

  • Title
  • Author
  • Subject
  • ID do livro

Definindo uma Estrutura

o defstructmacro no LISP permite que você defina uma estrutura de registro abstrata. odefstruct declaração define um novo tipo de dados, com mais de um membro para seu programa.

Para discutir o formato do defstructmacro, vamos escrever a definição da estrutura do livro. Podemos definir a estrutura do livro como -

(defstruct book 
   title 
   author 
   subject 
   book-id 
)

Observe

  • A declaração acima cria uma estrutura de livro com quatro named components. Portanto, todo livro criado será um objeto dessa estrutura.

  • Ele define quatro funções chamadas book-title, book-author, book-subject e book-book-id, que terá um argumento, uma estrutura de livro, e retornará os campos título, autor, assunto e book-id do livro objeto. Essas funções são chamadas deaccess functions.

  • O livro de símbolos se torna um tipo de dados e você pode verificá-lo usando o typep predicado.

  • Haverá também uma função implícita chamada book-p, que é um predicado e será verdadeiro se seu argumento for um livro e falso caso contrário.

  • Outra função implícita chamada make-book será criado, o que é um constructor, que, quando chamado, criará uma estrutura de dados com quatro componentes, adequada para uso com as funções de acesso.

  • o #S syntax refere-se a uma estrutura, e você pode usá-la para ler ou imprimir instâncias de um livro.

  • Uma função implícita chamada copybook de um argumento também é definida como isso. Ele pega um objeto de livro e cria outro objeto de livro, que é uma cópia do primeiro. Esta função é chamada decopier function.

  • Você pode usar setf para alterar os componentes de um livro, por exemplo

(setf (book-book-id book3) 100)

Exemplo

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(defstruct book 
   title 
   author 
   subject 
   book-id 
)

( setq book1 (make-book :title "C Programming"
   :author "Nuha Ali" 
   :subject "C-Programming Tutorial"
   :book-id "478")
)

( setq book2 (make-book :title "Telecom Billing"
   :author "Zara Ali" 
   :subject "C-Programming Tutorial"
   :book-id "501")
) 

(write book1)
(terpri)
(write book2)
(setq book3( copy-book book1))
(setf (book-book-id book3) 100) 
(terpri)
(write book3)

Quando você executa o código, ele retorna o seguinte resultado -

#S(BOOK :TITLE "C Programming" :AUTHOR "Nuha Ali" :SUBJECT "C-Programming Tutorial" :BOOK-ID "478")
#S(BOOK :TITLE "Telecom Billing" :AUTHOR "Zara Ali" :SUBJECT "C-Programming Tutorial" :BOOK-ID "501")
#S(BOOK :TITLE "C Programming" :AUTHOR "Nuha Ali" :SUBJECT "C-Programming Tutorial" :BOOK-ID 100)

Em termos gerais de linguagens de programação, um pacote é projetado para fornecer uma maneira de manter um conjunto de nomes separado de outro. Os símbolos declarados em um pacote não entrarão em conflito com os mesmos símbolos declarados em outro. Desta forma, os pacotes reduzem os conflitos de nomenclatura entre módulos de código independentes.

O leitor LISP mantém uma tabela de todos os símbolos encontrados. Ao encontrar uma nova seqüência de caracteres, ele cria um novo símbolo e o armazena na tabela de símbolos. Esta tabela é chamada de pacote.

O pacote atual é referido pela variável especial * pacote *.

Existem dois pacotes predefinidos no LISP -

  • common-lisp - contém símbolos para todas as funções e variáveis ​​definidas.

  • common-lisp-user- usa o pacote common-lisp e todos os outros pacotes com ferramentas de edição e depuração; é chamado cl-user em resumo

Funções de pacote em LISP

A tabela a seguir fornece as funções mais comumente usadas para criar, usar e manipular pacotes -

Sr. Não. Função e descrição
1

make-package nome do pacote e chave: apelidos: use

Ele cria e retorna um novo pacote com o nome do pacote especificado.

2

in-package nome do pacote e chave: apelidos: use

Torna o pacote atual.

3

in-package nome

Esta macro faz com que * pacote * seja definido para o pacote denominado nome, que deve ser um símbolo ou string.

4

find-package nome

Ele procura um pacote. O pacote com esse nome ou apelido é retornado; se tal pacote não existir, find-package retorna nil.

5

rename-package pacote new-name & new-nicknames opcionais

ele renomeia um pacote.

6

list-all-packages

Esta função retorna uma lista de todos os pacotes que existem atualmente no sistema Lisp.

7

delete-package pacote

Ele exclui um pacote.

Criando um Pacote LISP

o defpackagefunção é usada para criar um pacote definido pelo usuário. Possui a seguinte sintaxe -

(defpackage :package-name
   (:use :common-lisp ...)
   (:export :symbol1 :symbol2 ...)
)

Onde,

  • nome-do-pacote é o nome do pacote.

  • A palavra-chave: use especifica os pacotes de que este pacote precisa, ou seja, pacotes que definem funções usadas pelo código neste pacote.

  • A palavra-chave: export especifica os símbolos que são externos neste pacote.

o make-packagefunção também é usada para criar um pacote. A sintaxe para esta função é -

make-package package-name &key :nicknames :use

os argumentos e palavras-chave têm o mesmo significado de antes.

Usando um Pacote

Depois de criar um pacote, você pode usar o código neste pacote, tornando-o o pacote atual. oin-package macro torna um pacote atual no ambiente.

Exemplo

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(make-package :tom)
(make-package :dick)
(make-package :harry)
(in-package tom)
(defun hello () 
   (write-line "Hello! This is Tom's Tutorials Point")
)

(hello)
(in-package dick)
(defun hello () 
   (write-line "Hello! This is Dick's Tutorials Point")
)

(hello)
(in-package harry)
(defun hello () 
   (write-line "Hello! This is Harry's Tutorials Point")
)

(hello)
(in-package tom)
(hello)
(in-package dick)
(hello)
(in-package harry)
(hello)

When you execute the code, it returns the following result −

Hello! This is Tom's Tutorials Point
Hello! This is Dick's Tutorials Point
Hello! This is Harry's Tutorials Point

Deleting a Package

The delete-package macro allows you to delete a package. The following example demonstrates this −

Example

Create a new source code file named main.lisp and type the following code in it.

(make-package :tom)
(make-package :dick)
(make-package :harry)
(in-package tom)
(defun hello () 
   (write-line "Hello! This is Tom's Tutorials Point")
)

(in-package dick)
(defun hello () 
   (write-line "Hello! This is Dick's Tutorials Point")
)

(in-package harry)
(defun hello () 
   (write-line "Hello! This is Harry's Tutorials Point")
)

(in-package tom)
(hello)
(in-package dick)
(hello)
(in-package harry)
(hello)
(delete-package tom)
(in-package tom)
(hello)

When you execute the code, it returns the following result −

Hello! This is Tom's Tutorials Point
Hello! This is Dick's Tutorials Point
Hello! This is Harry's Tutorials Point
*** - EVAL: variable TOM has no value

In Common LISP terminology, exceptions are called conditions.

In fact, conditions are more general than exceptions in traditional programming languages, because a condition represents any occurrence, error, or not, which might affect various levels of function call stack.

Condition handling mechanism in LISP, handles such situations in such a way that conditions are used to signal warning (say by printing an warning) while the upper level code on the call stack can continue its work.

The condition handling system in LISP has three parts −

  • Signalling a condition
  • Handling the condition
  • Restart the process

Handling a Condition

Let us take up an example of handling a condition arising out of divide by zero condition, to explain the concepts here.

You need to take the following steps for handling a condition −

  • Define the Condition − "A condition is an object whose class indicates the general nature of the condition and whose instance data carries information about the details of the particular circumstances that lead to the condition being signalled".

    The define-condition macro is used for defining a condition, which has the following syntax −

    (define-condition condition-name (error)
       ((text :initarg :text :reader text))
    )

    New condition objects are created with MAKE-CONDITION macro, which initializes the slots of the new condition based on the :initargs argument.

    In our example, the following code defines the condition −

    (define-condition on-division-by-zero (error)
       ((message :initarg :message :reader message))
    )
  • Writing the Handlers − a condition handler is a code that are used for handling the condition signalled thereon. It is generally written in one of the higher level functions that call the erroring function. When a condition is signalled, the signalling mechanism searches for an appropriate handler based on the condition's class.

    Each handler consists of −

    • Type specifier, that indicates the type of condition it can handle
    • A function that takes a single argument, the condition

    When a condition is signalled, the signalling mechanism finds the most recently established handler that is compatible with the condition type and calls its function.

    The macro handler-case establishes a condition handler. The basic form of a handler-case −

    (handler-case expression error-clause*)

    Where, each error clause is of the form −

    condition-type ([var]) code)
  • Restarting Phase

    This is the code that actually recovers your program from errors, and condition handlers can then handle a condition by invoking an appropriate restart. The restart code is generally place in middle-level or low-level functions and the condition handlers are placed into the upper levels of the application.

    The handler-bind macro allows you to provide a restart function, and allows you to continue at the lower level functions without unwinding the function call stack. In other words, the flow of control will still be in the lower level function.

    The basic form of handler-bind is as follows −

    (handler-bind (binding*) form*)

    Where each binding is a list of the following −

    • a condition type
    • a handler function of one argument

    The invoke-restart macro finds and invokes the most recently bound restart function with the specified name as argument.

    You can have multiple restarts.

Example

In this example, we demonstrate the above concepts by writing a function named division-function, which will create an error condition if the divisor argument is zero. We have three anonymous functions that provide three ways to come out of it - by returning a value 1, by sending a divisor 2 and recalculating, or by returning 1.

Create a new source code file named main.lisp and type the following code in it.

(define-condition on-division-by-zero (error)
   ((message :initarg :message :reader message))
)
   
(defun handle-infinity ()
   (restart-case
      (let ((result 0))
         (setf result (division-function 10 0))
         (format t "Value: ~a~%" result)
      )
      (just-continue () nil)
   )
)
     
(defun division-function (value1 value2)
   (restart-case
      (if (/= value2 0)
         (/ value1 value2)
         (error 'on-division-by-zero :message "denominator is zero")
      )

      (return-zero () 0)
      (return-value (r) r)
      (recalc-using (d) (division-function value1 d))
   )
)

(defun high-level-code ()
   (handler-bind
      (
         (on-division-by-zero
            #'(lambda (c)
               (format t "error signaled: ~a~%" (message c))
               (invoke-restart 'return-zero)
            )
         )
         (handle-infinity)
      )
   )
)

(handler-bind
   (
      (on-division-by-zero
         #'(lambda (c)
            (format t "error signaled: ~a~%" (message c))
            (invoke-restart 'return-value 1)
         )
      )
   )
   (handle-infinity)
)

(handler-bind
   (
      (on-division-by-zero
         #'(lambda (c)
            (format t "error signaled: ~a~%" (message c))
            (invoke-restart 'recalc-using 2)
         )
      )
   )
   (handle-infinity)
)

(handler-bind
   (
      (on-division-by-zero
         #'(lambda (c)
            (format t "error signaled: ~a~%" (message c))
            (invoke-restart 'just-continue)
         )
      )
   )
   (handle-infinity)
)

(format t "Done."))

When you execute the code, it returns the following result −

error signaled: denominator is zero
Value: 1
error signaled: denominator is zero
Value: 5
error signaled: denominator is zero
Done.

Apart from the 'Condition System', as discussed above, Common LISP also provides various functions that may be called for signalling an error. Handling of an error, when signalled, is however, implementation-dependent.

Error Signalling Functions in LISP

The following table provides commonly used functions signalling warnings, breaks, non-fatal and fatal errors.

The user program specifies an error message (a string). The functions process this message and may/may not display it to the user.

The error messages should be constructed by applying the format function, should not contain a newline character at either the beginning or end, and need not indicate error, as the LISP system will take care of these according to its preferred style.

Sr.No. Function and Description
1

error format-string &rest args

It signals a fatal error. It is impossible to continue from this kind of error; thus error will never return to its caller.

2

cerror continue-format-string error-format-string &rest args

It signals an error and enters the debugger. However, it allows the program to be continued from the debugger after resolving the error.

3

warn format-string &rest args

it prints an error message but normally doesn't go into the debugger

4

break &optional format-string &rest args

It prints the message and goes directly into the debugger, without allowing any possibility of interception by programmed error-handling facilities

Example

In this example, the factorial function calculates factorial of a number; however, if the argument is negative, it raises an error condition.

Create a new source code file named main.lisp and type the following code in it.

(defun factorial (x)
   (cond ((or (not (typep x 'integer)) (minusp x))
      (error "~S is a negative number." x))
      ((zerop x) 1)
      (t (* x (factorial (- x 1))))
   )
)

(write(factorial 5))
(terpri)
(write(factorial -1))

When you execute the code, it returns the following result −

120
*** - -1 is a negative number.

Common LISP predated the advance of object-oriented programming by couple of decades. However, it object-orientation was incorporated into it at a later stage.

Defining Classes

The defclass macro allows creating user-defined classes. It establishes a class as a data type. It has the following syntax −

(defclass class-name (superclass-name*)
   (slot-description*)
   class-option*))

The slots are variables that store data, or fields.

A slot-description has the form (slot-name slot-option*), where each option is a keyword followed by a name, expression and other options. Most commonly used slot options are −

  • :accessor function-name

  • :initform expression

  • :initarg symbol

For example, let us define a Box class, with three slots length, breadth, and height.

(defclass Box () 
   (length 
   breadth 
   height)
)

Providing Access and Read/Write Control to a Slot

Unless the slots have values that can be accessed, read or written to, classes are pretty useless.

You can specify accessors for each slot when you define a class. For example, take our Box class −

(defclass Box ()
   ((length :accessor length)
      (breadth :accessor breadth)
      (height :accessor height)
   )
)

You can also specify separate accessor names for reading and writing a slot.

(defclass Box ()
   ((length :reader get-length :writer set-length)
      (breadth :reader get-breadth :writer set-breadth)
      (height :reader get-height :writer set-height)
   )
)

Creating Instance of a Class

The generic function make-instance creates and returns a new instance of a class.

It has the following syntax −

(make-instance class {initarg value}*)

Example

Let us create a Box class, with three slots, length, breadth and height. We will use three slot accessors to set the values in these fields.

Create a new source code file named main.lisp and type the following code in it.

(defclass box ()
   ((length :accessor box-length)
      (breadth :accessor box-breadth)
      (height :accessor box-height)
   )
)
(setf item (make-instance 'box))
(setf (box-length item) 10)
(setf (box-breadth item) 10)
(setf (box-height item) 5)
(format t "Length of the Box is ~d~%" (box-length item))
(format t "Breadth of the Box is ~d~%" (box-breadth item))
(format t "Height of the Box is ~d~%" (box-height item))

Quando você executa o código, ele retorna o seguinte resultado -

Length of the Box is 10
Breadth of the Box is 10
Height of the Box is 5

Definindo um Método de Classe

o defmethodmacro permite que você defina um método dentro da classe. O exemplo a seguir estende nossa classe Box para incluir um método denominado volume.

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(defclass box ()
   ((length :accessor box-length)
      (breadth :accessor box-breadth)
      (height :accessor box-height)
      (volume :reader volume)
   )
)

; method calculating volume   

(defmethod volume ((object box))
   (* (box-length object) (box-breadth object)(box-height object))
)

 ;setting the values 

(setf item (make-instance 'box))
(setf (box-length item) 10)
(setf (box-breadth item) 10)
(setf (box-height item) 5)

; displaying values

(format t "Length of the Box is ~d~%" (box-length item))
(format t "Breadth of the Box is ~d~%" (box-breadth item))
(format t "Height of the Box is ~d~%" (box-height item))
(format t "Volume of the Box is ~d~%" (volume item))

Quando você executa o código, ele retorna o seguinte resultado -

Length of the Box is 10
Breadth of the Box is 10
Height of the Box is 5
Volume of the Box is 500

Herança

LISP permite que você defina um objeto em termos de outro objeto. Isso é chamadoinheritance.Você pode criar uma classe derivada adicionando recursos novos ou diferentes. A classe derivada herda as funcionalidades da classe pai.

O exemplo a seguir explica isso -

Exemplo

Crie um novo arquivo de código-fonte denominado main.lisp e digite o seguinte código nele.

(defclass box ()
   ((length :accessor box-length)
      (breadth :accessor box-breadth)
      (height :accessor box-height)
      (volume :reader volume)
   )
)

; method calculating volume   
(defmethod volume ((object box))
   (* (box-length object) (box-breadth object)(box-height object))
)
  
;wooden-box class inherits the box class  
(defclass wooden-box (box)
((price :accessor box-price)))

;setting the values 
(setf item (make-instance 'wooden-box))
(setf (box-length item) 10)
(setf (box-breadth item) 10)
(setf (box-height item) 5)
(setf (box-price item) 1000)

; displaying values
(format t "Length of the Wooden Box is ~d~%" (box-length item))
(format t "Breadth of the Wooden Box is ~d~%" (box-breadth item))
(format t "Height of the Wooden Box is ~d~%" (box-height item))
(format t "Volume of the Wooden Box is ~d~%" (volume item))
(format t "Price of the Wooden Box is ~d~%" (box-price item))

Quando você executa o código, ele retorna o seguinte resultado -

Length of the Wooden Box is 10
Breadth of the Wooden Box is 10
Height of the Wooden Box is 5
Volume of the Wooden Box is 500
Price of the Wooden Box is 1000