Baseado em:
Common LISP Hints
Geoffrey J. Gordon
<ggordon@cs.cmu.edu>
Função com múltiplos comandos em seu corpo
Número Variável de Argumentos para Funções
Número Indefinido de Parâmetros
Passagem de Parâmetros por Nome
Dynamic Scoping - Escopo Dinâmico
Utilidade de
Funcall, apply e Mapcar
Exemplos de Fixação:
Igualdade e Identidade
Algumas Funções de Lista Úteis
Utilizando Emacs/X-Emacs para programar LISP
Um símbolo é somente um string de
caracteres.
Alguns exemplos:
a
b
c1
foo
bar
baaz-quux-garply
Algumas operações com símbolos seguem abaixo. Coisas após o prompt “>” são as que você digita para o interpretador lisp. Tudo após um “;” é um comentário.
> (setq a 5) ;store a number as the value of a symbol
5
> a ;take the value of a symbol
5
> (let ((a
6)) a) ;bind the value of a symbol
temporarily to 6
6
> a ;the value returns to 5 once the
;let is finished
5
> (+ a 6) ;use the value of a symbol as
an argument to a function
11
> b ;try to take the value of a
symbol which has no value
Error: Attempt
to take the value of the unbound symbol B
Há dois símbolos especiais, t e nil. O valor de t é definido sempre como sendo t. nil é definido como sendo sempre nil.
LISP utiliza t e nil para
representar verdadeiro e falso. Exemplo
no IF:
> (if t 5 6)
5
> (if nil 5
6)
6
> (if 4 5 6)
5
O último exemplo é estranho, mas está correto. nil significa falso e qualquer outra coisa verdadeiro. Usamos t somente para clareza.
Símbolos como nil e t são chamados símbolos auto-avaliantes, porque avaliam para si mesmos.
Há toda uma classe de símbolos
auto-avaliantes chamados palavras-chave. Qualquer símbolo cujo nome inicia com
dois pontos é uma palavra-chave. Exemplos:
>
:this-is-a-keyword
:THIS-IS-A-KEYWORD
> :so-is-this
:SO-IS-THIS
> :me-too
:ME-TOO
Um inteiro é um string de dígitos opcionalmente precedido de um + ou -.
Um real parece com um inteiro, só que possui um ponto decimal e pode opcionalmente ser escrito em notação científica.
Um racional se parece com dois inteiros com um / entre eles.
LISP suporta números complexos que são escritos #c(r i)
Exemplos:
17
-34
+6
3.1415
1.722e-15
#c(1.722e-15 0.75)
As funções aritméticas padrão são todas avaliáveis: +, -, *, /, floor, ceiling, mod, sin, cos, tan, sqrt, exp, expt etc
Todas elas aceitam qualquer número como argumento:
> (+ 3 3/4) ;type contagion
15/4
> (exp 1) ;e
2.7182817
> (exp 3) ;e*e*e
20.085537
> (expt 3
4.2) ;exponent with
a base other than e
100.90418
> (+ 5 6 7 (*
8 9 10)) ;the fns +-*/ all accept
multiple arguments
Não existe limite para o valor absoluto de um inteiro exceto a memória do computador. Evidentemente cálculos com inteiros ou racionais imensos podem ser muito lentos.
n sx;'''-
Um cons é somente um registro de dois campos. Os campos são chamados de ‘car’ e ‘cdr’ por razões históricas: na primeira máquina onde LISP foi implementado havia duas instruções assembler CAR e CDR ‘contents of address register’ e ‘contents of decrement register’.
Conses foram implementados utilizando-se esses dois registradores:
> (cons 4 5) ;Allocate a cons. Set the car
to 4 and the cdr to 5.
(4 . 5)
> (cons (cons 4 5) 6)
((4 . 5) . 6)
> (car (cons 4 5))
4
> (cdr (cons 4 5))
5
Você pode construir muitas estruturas de dados a partir de conses. A mais simples com certeza é a lista encadeada:
• o car de cada cons aponta para um dos elementos da lista e
• o cdr aponta ou para outro cons ou para nil.
Uma lista assim pode ser criada com a função de lista:
> (list 4 5 6)
(4 5 6)
Observe que LISP imprime listas de uma forma especial: ele omite alguns dos pontos e parênteses.
A regra é: se o cdr de um cons é nil, lisp não se preocupa em imprimir o ponto ou o nil. Se o cdr de cons A é cons B, então lisp não se preocupa em imprimir o ponto para A nem o parênteses para B:
> (cons 4 nil)
(4)
> (cons 4 (cons 5 6))
(4 5 . 6)
> (cons 4 (cons 5 (cons 6 nil)))
(4 5 6)
O último exemplo corresponde ao (list 4 5 6) anterior. Note que nil corresponde à lista vazia.
• O car e cdr de nil são definidos como nil.
• O car de um átomo é o próprio átomo.
• O cdr de um átomo é nil.
Se você armazena uma lista em uma variável, pode fazê-la funcionar como uma pilha:
> (setq a nil)
NIL
> (push 4 a)
(4)
> (push 5 a)
(5 4)
> (pop a)
5
> a
(4)
> (pop a)
4
> (pop a)
NIL
> a
NIL
Vimos
exemplos de funções acima. Aqui mais alguns:
> (+ 3 4 5 6)
;this
function takes any
18 ;number of arguments
> (+ (+ 3 4) (+ (+ 4 5) 6)) ;isn't prefix notation fun?
22
> (defun foo (x y) (+ x y 5))
FOO
> (foo 5 0) ;chamando a função
10
> (defun fact
(x) (if (> x 0) (* x (fact (- x 1)))
1 ) )
FACT
> (fact 5)
120
> (defun a (x) (if (= x 0) t (b (- x))))
A
> (defun b
(x) (if (> x 0) (a (- x 1)) (a (+ x 1))))
B
> (a 5)
T
Função com
múltiplos comandos em seu corpo
> (defun bar
(x)
(setq x (* x 3))
(setq x (/ x 2))
(+ x 4)
)
BAR
> (bar 6)
13
• O valor retornado, como em um método em Smalltalk, é
sempre o valor da última expressão executada.
Quando
nós definimos foo, nós lhe atribuímos dois
argumentos, x e y. Quando chamamos foo, necessitamos
prover valores para esses dois argumentos. O primeiro será o valor de x durante
a duração da chamada a foo, o segundo o valor de y
durante a duração da chamada a foo. Em LISP, a
maioria das variáveis são escopadas lexicamente. Isto
significa que se foo chama bar e bar tenta
referenciar x, bar não obterá os valor de x de foo.
> (defun foo
(x y) (+ x y 5))
FOO
O
processo de se associar um valor a um símbolo durante um certo escopo léxico é
chamado em LISP de ateamento. x estava atado ao
escopo de foo.
Número Variável de Argumentos para Funções
Você
pode especificar também argumentos opcionais para funções. Qualquer argumento
após o símbolo &optional é opcional:
> (defun bar
(x &optional y) (if y x 0))
BAR
É
perfeitamente legal chamar a função BAR com um ou dois argumentos. Se for
chamada com um argumento, x será atado ao valor deste argumento e y será atado
a NIL.
> (bar 5)
0
> (bar 5 t)
5
Se
for chamada com dois argumentos, x e y serão atados aos valores do primeiro e
segundo argumento respectivamente.
A
função BAAZ possui dois argumentos opcionais, porém especifica um valor default para cada um deles.
> (defun baaz
(&optional (x 3) (z 10)) (+ x z))
BAAZ
> (baaz 5)
15
> (baaz 5 6)
11
> (baaz)
13
Se
quem a chama especificar somente um argumento, z será atado a 10 ao invés de
NIL. Se nenhum argumento for especificado, x será atado a 3
e z a 10.
Número
Indefinido de Parâmetros
Você
pode fazer a sua função aceitar um número indefinido de parâmetros terminando a
sua lista de parâmetros com o parâmetro &rest.
LISP vai coletar todos os argumentos que não sejam contabilizados para algum
argumento formal em uma lista a atá-la ao parâmetro &rest :
> (defun foo
(x &rest y) y)
FOO
> (foo 3)
NIL
> (foo 4 5 6)
(5 6)
Passagem de
Parâmetros por Nome
Existe
ainda um tipo de parâmetro opcional chamado de parâmetro de palavra-chave. São parâmtros que quem chama pode passar em qualquer ordem,
pois os valores são passados precedidos pelo nome do parâmetro formal a que se
referem:
> (defun foo
(&key x y) (cons x y))
FOO
> (foo :x 5 :y 3)
(5 . 3)
> (foo :y 3
:x 5)
(5 . 3)
> (foo :y 3)
(NIL . 3)
> (foo)
(NIL)
Um parâmetro &key pode ter um valor default
também:
> (defun foo
(&key (x 5)) x)
FOO
> (foo :x 7)
7
> (foo)
5
Algumas
funções podem provocar uma saída. A mais simples é print,
que imprime o seu argumento e então o retorna.
> (print 3)
3
3
O
primeiro 3 acima foi impresso, o segundo retornado.
Se
você deseja um output mais complexo, você necessita utilizar format:
>(format t "An atom: ~S~%and a list:
~S~%and an integer:~D~%"
nil (list 5) 6)
An atom: NIL
and a list: (5)
and an integer: 6
O primeiro argumento a format é ou t, ou NIL ou
um arquivo.
• T especifica que a saída deve ser dirigida
para o terminal,
• NIL especifica que não deve ser impresso
nada, mas que format deve retornar um string com o conteúdo ao invés,
• uma referência a um arquivo especifica o
arquivo para onde a saída vai ser redirigida.
O
segundo argumento é um template de formatação, o qual
é um string contendo opcionalmente diretivas de
formatação, de forma similar à Linguagem “C”: "An
atom: ~S~%and a list: ~S~%and an
integer:~D~%"
Todos
os argumentos restantes devem ser referenciados a partir do
string de formatação.
• As diretivas de
formatação do string serão repostas por LISP por caracteres apropriados com
base nos valores dos outros parâmetros a que eles se referem e então imprimir o
string resultante.
• Format sempre
retorna NIL, a não ser que seu primeiro argumento seja NIL, caso em que não
imprime nada e retorna o string resultante.
No
exemplo acima, há três diretivas de formatação: ~S, ~D e ~%.:
• A primeira,~S,
aceita qualquer objeto LISP e é substituída por uma representação passível de
ser impressa deste objeto (a mesma produzida por print).
• A segunda, ~D,
só aceita inteiros.
• A terceira,
~%, não acita nada. Sempre é reposta por uma quebra de linha.
• Outra diretiva
útil é ~~, que é substituída por um simples ~.
• Veja no manual
do LISP muitas, muitas outras diretivas de formatação...
As
coisas que você digita para o interpretador LISP são chamadas forms. O interpretador repetidamente lê um form, o avalia e imprime o resultado.
• Este procedimento é chamado read-eval-print loop.
Alguns
forms vão provocar erros. Após um erro, LISP vai pô-lo/la no ambiente do debugger.
Debuggers
de interpretadores LISP são muito diferentes entre si. A maioria aceita um “help” ou “:help”
para ajudá-lo/la na debugação.
No
debugador do CLISP você pode sair dando um Control-Z (em DOS) ou Control-D
(em Unix).
Em
geral, um form é ou um átomo (p.ex.:
um símbolo, um inteiro ou um string) ou uma lista.
Se
o form for um átomo, LISP o avalia imediatamente.
Símbolos avaliam para seu valor, inteiros e strings
avaliam para si mesmos.
Se
o form for uma lista, LISP trata o seu primeiro
elemento como o nome da função, avaliando os elementos restantes de forma
recursiva. Então chama a função com os valores dos elementos restantes como
argumentos.
Por exemplo, se LISP vê o form (+ 3 4):
Irá tratar + como o nome da função.
Ele então avaliará 3 para obter 3, avaliará 4 para obter 4 e finalmente chamará + com 3 e 4 como argumentos.
A função + retornará 7, que LISP então imprime.
O top-level loop provê algumas
outras conveniências.
Uma
particularmente interessante é a habilidade de falar a respeito dos resultados
de forms previamente digitados: LISP sempre salva os
seus três resultados mais recentes. Ele os armazena sob os símbolos *, ** e
***.
> 3
3
> 4
4
> 5
5
> ***
3
> ***
4
> ***
5
> **
4
> *
4
Há
um número de forms especiais que se parecem com
chamadas a funções mas não o são. Um form muito útil é o form
aspas. As aspas prevêm um argumento de ser avaliado.
> (setq a 3)
3
> a
3
> (quote a)
A
> 'a ;'a is an abbreviation for
(quote a)
A
Outro
form especial similar é o form function.
Function provoca que seu argumento seja interpretado como uma função ao invés de ser avaliado:
> (setq + 3)
3
> +
3
> '+
+
> (function
+)
#<Function +
@ #x-fbef9de>
> #'+ ;#'+ is an abbreviation for
(function +)
#<Function + @ #x-fbef9de>
O form especial function é útil
quando você deseja passar uma função como parâmetro para outra função. Mais
tarde apresentaremos alguns exemplos de funções que aceitam outras funções como
parâmetros.
Binding é
uma atribuição escopada lexicamente. Ela ocorre com
as variáveis de uma lista de parâmetros de uma função sempre que a função é
chamada: os parâmetros formais são atados aos parâmetros reais pela duração da
chamada à função.
Você
pode também amarrar variáveis em qualquer parte de um programa com o form especial let:
(let ((var1 val1)
(var2 val2)
...
)
body
)
Let ata var1 a val1, var2
a val2, e assim por diante; então executa os comandos
de seu corpo. O corpo de um let segue as mesmas
regras de um corpo de função:
> (let ((a
3)) (+ a 1))
4
> (let ((a 2)
(b 3) (c 0)) (setq c (+ a b)) c )
5
> (setq c 4)
4
> (let ((c
5)) c)
5
> c
4
Ao
invés de (let ((a nil) (b nil)) ...)você pode escrever
(let (a b) ...).
Os
valores val1, val2, etc.
dentro de um let não podem referenciar as variáveis var1, var2, etc. que o let está atando:
> (let ((x 1)
(y (+ x 1)))
y
)
Error: Attempt
to take the value of the unbound symbol X
Se
o símbolo x já possui um valor, coisas estranhas podem acontecer:
> (setq x 7)
7
> (let ((x 1)
(y (+ x 1)))
y
)
8
O form especial let* é semelhante,
só que permite que sejam referenciadas variáveis definidas anteriormente:
> (setq x 7)
7
> (let* ((x
1)
(y (+ x 1)))
y
)
2
O form
(let* ((x a)
(y b))
...
)
é equivalente a:
(let ((x a))
(let ((y b))
...
) )
Dynamic Scoping - Escopo Dinâmico
Os
forms let e let* provêem escop léxico, que é
o que você está acostumado quando programa em “C” ou PASCAL.
Escopo
dinâmico é o que você tem em BASIC: se você atribui um valor a uma variável escopada dinamicamente, TODA menção desta variável vai
retornar aquele valor até que você atribua outro valor à mesma variável.
Em
LISP variáveis dinamicamente escopadas são variáveis
especiais. Você pode criar uma variável especial através do form
defvar.
Abaixo
seguem alguns exemplos de variáveis escopadas
lexicamente e dinamicamente.
1. Neste exemplo a função check-regular referencia uma variável
regular (ie, lexicamente escopada).
Como check-regular está lexicamente fora do let que ata regular, check-regular
retorna o valor global da variável:
> (setq regular 5)
5
> (defun check-regular () regular)
CHECK-REGULAR
> (check-regular)
5
> (let ((regular 6)) (check-regular))
5
2. Neste exemplo, a função check-special referencia uma variável especial (ie, escopada dinamicamente).
Uma vez que a chamada a check-special é temporariamente dentro do let que amarra special, check-special retorna o valor local da variável:
> (defvar
*special* 5)
*SPECIAL*
> (defun
check-special () *special*)
CHECK-SPECIAL
>
(check-special)
5
> (let
((*special* 6)) (check-special))
6
Por
uma questão de convenção, o nome de uma variável especial começa e termina com
*.
Variáveis
especiais são principalmente usadas como variáveis globais.
A
função make-array faz um array.
A função aref acessa seus elementos. Todos os elementos
de um array são inicialmente setados
para nil:
> (make-array '(3 3))
#2a((NIL NIL NIL) (NIL NIL NIL) (NIL NIL NIL))
> (aref * 1
1)
NIL
> (make-array
4) ;1D arrays don't need the extra parens
#(NIL NIL NIL NIL)
Índices
de um array sempre começam em 0
Um string
é uma seqüência de caracteres entre aspas duplas. LISP representa um string como um array de tamanho
variável de caracteres.
Você
pode escrever um string que contém a aspa dupla,
precedendo a de um backslash \ . Um backslash duplo \\ está para um \ :
"abcd" has 4 characters
"\"" has 1 character
"\\" has 1 character
Algumas funções para manipulação de strings:
>
(concatenate 'string "abcd" "efg")
"abcdefg"
> (char
"abc" 1)
#\b
;LISP
writes characters preceded by #\
> (aref
"abc" 1)
#\b ;remember,
strings are really arrays
A função concatenate
pode trabalhar sobre qualquer tipo de seqüência:
>
(concatenate 'string '(#\a #\b) '(#\c))
"abc"
>
(concatenate 'list "abc" "de")
(#\a #\b #\c #\d #\e)
> (concatenate 'vector '#(3 3 3) '#(3 3 3))
#(3 3 3 3 3 3)
Estruturas
LISP são análogas a structs em “C” ou records em PASCAL:
> (defstruct
foo
bar
baaz
quux
)
FOO
Este
exemplo define um tipo de dado chamado FOO contendo 3
campos.
Define
também 4 funções que operam neste tipo de dado:
make-foo,
foo-bar, foo-baaz, and foo-quux.
A primeira cria um novo
objeto do tipo FOO.
As outras acessam os campos de um objeto do tipo FOO.
> (make-foo)
#s(FOO :BAR NIL
:BAAZ NIL :QUUX NIL)
> (make-foo
:baaz 3)
#s(FOO :BAR NIL
:BAAZ 3 :QUUX NIL)
> (foo-bar *)
NIL
> (foo-baaz **)
3
A
função make-foo pode tomar um argumento para cada um
dos campos da estrutura do tipo. As funções de acesso a campo tomam cada uma um
argumento.
Veja abaixo como setar os campos de uma estrutura.
Alguns
forms em LISP naturalmente definem uma locação na
memória.
Por
exemplo, se x é uma estrutura do tipo FOO, então (foo-bar
x) define o campo BAR do valor de x.
Ou,
se o valor de y é um array unidimensional, então (aref y 2) define o terceiro
elemento de y.
O form especial setf usa seu
primeiro argumento para definir um lugar na memória, avalia o seu segundo
argumento e armazena o valor resultante na locação de memória resultante:
> (setq a
(make-array 3))
#(NIL NIL NIL)
> (aref a 1)
NIL
> (setf (aref a 1) 3)
3
> a
#(NIL 3 NIL)
> (aref a 1)
3
> (defstruct
foo bar)
FOO
> (setq a
(make-foo))
#s(FOO :BAR NIL)
> (foo-bar a)
NIL
> (setf (foo-bar a) 3)
3
> a
#s(FOO :BAR 3)
> (foo-bar a)
3
Setf é a
única maneira de se setar os valores de um array ou os campos de uma estrutura.
Alguns
exemplos de setf e funções relacionadas:
> (setf a
(make-array 1)) ;setf
on a variable is equivalent to setq
#(NIL)
> (push 5
(aref a 1)) ;push
can act like setf
(5)
> (pop (aref
a 1)) ;so can
pop
5
> (setf (aref
a 1) 5)
5
> (incf (aref
a 1)) ;incf
reads from a place, increments,
6 ;and writes
back
> (aref a 1)
6
LISP
usa o símbolo auto-avaliante NIL para significar
FALSO. Qualquer outra coisa significa VERDADEIRO.
Nós
usualmente utilizaremos o símbolo auto-avaliante t
para significar TRUE.
LISP
provê uma série de funções booleanas-padrão, como and, or e not.
Os conetivos and e or são curto-circuitantes. AND não vai avaliar quaisquer
argumentos à direita daquele que faz a função avaliar para NIL, enquanto OR não
avalia nenhum à direita do primeiro verdadeiro.
LISP
também provê uma série de forms para execução
condicional. O mais simples é o IF, onde o primeiro argumento determina se o
segundo ou o terceiro será avaliado.
Exemplos:
> (if t 5 6)
5
> (if nil 5
6)
6
> (if 4 5 6)
5
Se
você necessita colocar mais de um comando em uma das cláusulas, então use o form progn. Progn
executa cada comando em seu corpo e retorna o valor do último.
> (setq a 7)
7
> (setq b 0)
0
> (setq c 5)
5
> (if (> a
5)
(progn
(setq a (+ b 7))
(setq b (+ c 8)))
(setq b 4)
)
13
Um
if statement que não possui uma clausula then ou uma cláusula else pode
ser escrito utilizando-se when ou unless:
> (when t 3)
3
> (when nil
3)
NIL
> (unless t
3)
NIL
> (unless nil 3)
3
When e unless, ao contrário de if, aceitam qquer
número de comandos em seus corpos:
(when x a b c) é
equivalente a(if x (progn a b c)).
> (when t
(setq a 5)
(+ a 6)
)
11
Condicionais
mais complexos podem ser construídos através do form cond, que é equivalente a if ... else if ... fi.
Um
cond consiste de símbolo con
seguido por um número de cláusulas-cond, cada qual é
uma lista. O primeiro elemento de uma cláusula-cond é
a condição, os elementos restantes são a ação.
O cond encontra a primeira cláusula que avalia para true, executando a ação respectiva e retornando o valor
resultante. Nenhuma das restantes é avaliada.
> (setq a 3)
3
> (cond
((evenp a) a) ;if a is even return a
((> a 7) (/ a 2));else if a is bigger than 7
return a/2
((< a 5) (- a 1));else if a is smaller
than 5 return a-1
(t 17) ;else return 17
)
2
Se não há nenhuma ação na cláusula cond selecionada, cond retorna o valor verdadeiro:
> (cond ((+ 3 4)))
7
O comando LISP case é semelhante a um “C” switch statement:
> (setq x 'b)
B
> (case x
(a 5)
((d e) 7)
((b f) 3)
(otherwise 9)
)
3
A
cláusula otherwise significa que se x não for nem a,
b, d, e, ou f, o case statement vai retornar 9.
...existem MACROS em
LISP para definir blocos. Uma muito usada é prog. Prog permite, entre outras coisas, a DECLARAÇÃO explícita
de variáveis locais, além de retorno
explícito:
(defun F3.2.17a nil
(prog (i j) ;define i e j como variáveis
;locais inicializadas com nil
(setq i (read))
(setq j (read)
(return
(print (- i j)))
)
)
A construção de iteração
mais simples em LISP é loop: um loop repetidamente executa seu corpo até que
ele encontre um form especial do tipo return:
> (setq a 4)
4
> (loop
(setq a (+ a 1))
(when (> a 7) (return a))
)
8
> (loop
(setq a (- a 1))
(when (< a 3) (return))
)
NIL
O
próximo mais simples é a dolist: Uma dolist ata uma variável aos elementos de uma lista na sua
ordem e termina quando encontra o fim da lista:
> (dolist (x
'(a b c)) (print x))
A
B
C
NIL
Dolist
sempre retorna NIL como valor. Observe que o valor de X no exemplo acima nunca
é NIL, o valor NIL abaixo do C é o NIL retornado por dolist,
impresso pelo read-eval-print loop.
A
primitiva de iteração mais complicada é o do. Um comando do tem a seguinte
forma:
> (do ((x 1 (+ x 1))
;variável x, com valor inicial 1
(y 1 (* y 2)) ;variável y, com
valor inicial 1
)
((> x 5) y) ;retorna
valor de y quando x > 5
(print y) ;corpo
(print 'working) ;corpo
)
1
WORKING
2
WORKING
4
WORKING
8
WORKING
16
WORKING
32
• A primeira parte do do (sublinhada) especifica quais
variáveis que devem ser atadas, quais são os seus valores iniciais e como eles
devem ser atualizados.
• A segunda parte especifica uma condição de término (em itálico) e um
valor de retorno.
• A última parte é o corpo.
Um
form do ata suas variáveis aos seus valores iniciais
da mesma forma como um let e então checa a condição
de término. Enquanto a condição for falsa, executa o corpo repetidamente.
Quando a condição se torna verdadeira, ele retorna o valor do form de valor-de-retorno.
O form do* é para o do o que let* épara let.
O form especial return mencionado na seção de iteração é um exemplo de um return não-local. Outro exemplo é o form return-from, o qual retorna um valor da função que o envolve:
> (defun foo
(x)
(return-from foo 3)
x
)
FOO
> (foo 17)
3
Na
verdade, o form return-from
pode retornar de qualquer bloco nomeado.
Na
verdade, funções são os únicos blocos nomeados por default.
Você pode criar um bloco nomeado com o form especial block:
> (block foo
(return-from foo 7)
3
)
7
O form especial return pode
retornar de qualquer bloco nomeado NIL. Loops são por
default nomeados NIL, mas você pode fazer também seus
próprios blocos NIL-nomeados:
> (block nil
(return 7)
3
)
7
Outro form que causa uma saída não-local é o form error:
> (error
"This is an error")
Error: This is an error
O form error aplica format aos seus argumentos e então coloca você no ambiente
do debugador.
Como
foi dito antes, em LISP também funções podem ser argumentos para funções. Aqui
algumas funções que pedem como argumento uma função:
> (funcall #'+ 3 4)
7
> (apply #'+
3 4 '(3 4))
14
> (mapcar
#'not '(t nil t nil t nil))
(NIL T NIL T NIL
T)
• Funcall chama seu primeiro argumento com os argumentos restantes como argumentos deste.
• Apply é semelhante a Funcall, exceto que seu argumento final deverá ser uma lista. Os elementos desta lista são tratados como se fossem argumentos adicionais ao Funcall.
• O primeiro argumento a mapcar deve ser uma função de um argumento. mapcar aplica esta função a cada elemento de uma lista dada e coleta os resulatdos em uma outra lista.
Utilidade de Funcall, apply e Mapcar
Funcall e
apply são principalmente úteis quando o seu primeiro
argumento é uma variável.
Por
exemplo, uma máquina de inferência poderia tomar uma função heurística e
utilizar funcall ou apply
para chamar esta função sobre uma descrição de um estado.
As
funções de ordenação a serem descritas mais tarde utilizam funcall
para chamar as suas funções de comparação.
Mapcar,
juntamente com funções sem nome (veja abaixo) pode substituir muitos laços.
Se
você somente deseja criar uma função temporária e não deseja perder tempo
dando-lhe um nome, lambda é justamente o que você
precisa.
> #'(lambda (x) (+ x 3))
(LAMBDA (X) (+ X 3))
> (funcall * 5) ;observe que neste contexto o * é uma
;variável que representa o último form
;que foi digitado...
8
A
combinação de lambda e mapcar
pode substituir muitos laços. Por exemplo, os dois forms
seguintes são equivalentes:
> (do ((x '(1 2 3 4 5) (cdr x))
(y nil))
((null x) (reverse y))
(push (+ (car x) 2) y)
)
(3 4 5 6 7)
> (mapcar #'(lambda (x) (+ x 2)) '(1 2 3 4 5))
(3 4 5 6 7)
LISP provê duas primitivas para ordenação: sort e stable-sort.
> (sort '(2 1
5 4 6) #'<)
(1 2 4 5 6)
> (sort '(2 1
5 4 6) #'>)
(6 5 4 2 1)
O
primeiro argumento para sort é uma lista, o segundo é
a função de comparação. A função de comparação não garante estabilidade: se há
dois elementos a e b tais que (and (not (< a b)) (not (< b
a))), sort vai arranjá-los de qualquer maneira.
A função stable-sort é exatamento como sort, só que ela garante que dois elementos equivalentes vão aparecer na lista ordenada exatamente na mesma ordem em que aparecem lista original.
Seja
cuidadoso: sort tem permissão para destruir o seu
argumento. Por isso, se você deseja manter a lista original, copie-a com copy-list ou copy-seq.
LISP
tem muitos conceitos diferentes de igualdade. Igualdade numérica é denotada por
=.
Como
em Smalltalk ou em Prolog, existem os conceitos de
identidade (mesmo objeto) e igualdade (objetos distintos, porém iguais).
Dois
símbolos são eq se e somente se eles forem idênticos
(identidade). Duas cópias da mesma lista não são eq
(são dois objetos diferentes) mas são equal (iguais).
> (eq 'a 'a)
T
> (eq 'a 'b)
NIL
> (= 3 4)
T
> (eq '(a b c) '(a b c))
NIL
> (equal '(a b c) '(a b c))
T
> (eql 'a 'a)
T
> (eql 3 3)
T
O
predicado eql é equivalente a eq
para símbolos e a = para números. É a identidade que serve tanto para números
como para símbolos.
O
predicado equal é equivalente a eql
para símbolos e números.
Ele
é verdadeiro para dois conses, se e somente se, seus cars são equal e seus cdrs são equal.
Ele
é verdadeiro para duas estruturas se e somente se as estruturas forem do mesmo
tipo e seus campos correspondentes forem equal.
Exemplos de Fixação: Igualdade e Identidade
Observe:
> (setq X ´(A B))
(A B)
> (setq Y ´(A B))
(A B)
> (equal X Y) ;X e Y são iguais
T
> (eq X Y) ;X e Y não são idênticos
NIL
> (setq X ´(A B))
(A B)
> (setq Y X)
(A B)
> (equal X Y) ;X e Y são iguais
T
> (eq X Y) ;X e Y agora são idênticos
T
Algumas Funções de Lista Úteis
Todas
as funções abaixo manipulam listas:
>
(append '(1 2 3) '(4 5 6)) ;concatena
listas
(1
2 3 4 5 6)
>
(reverse '(1 2 3))
;reverte os elementos
(3
2 1)
>
(member 'a '(b d a c))
;pertinência a conjunto
;retorna primeira cauda
(A
C) ;cujo car
é o elemento
;desejado
> (find 'a '(b d a c)) ;outro set membership
A
>
(find '(a b) '((a d) (a d e) (a b d e) ()) :test #'subsetp)
(A
B D E) ;find é mais
flexível
> (subsetp '(a b) '(a d e)) ;set containment
NIL
> (intersection '(a b c)
'(b)) ;set intersection
(B)
> (union '(a) '(b)) ;set union
(A
B)
>
(set-difference '(a b) '(a)) ;diferença
de conjuntos
(B)
Subsetp, intersection, union, e set-difference todos assumem que cada
argumento não contém elementos duplicados. (subsetp
'(a a) '(a b b)) é permitido falhar, por exemplo.
Find, subsetp, intersection, union, e set-difference podem
todos tomar um argumento :test.
Por default, todos usam eql.
Utilizando
Emacs/X-Emacs para programar LISP
Você
pode usar o Emacs ou X-Emacs
para editar código LISP: a maioria dos Emacses
colocam-se automaticamente em modo-LISP quando
você carrega um arquivo que termina em .lisp,
mas se o seu não o faz, você pode digitar M-x lisp-mode
(ALT-x lisp-mode ou G-x lisp-mode).
Você
pode rodar LISP sob Emacs tambèm,
usando-o como um ambiente de programação: certifique-se de que há um comando
chamado "lisp" que roda seu LISP favorito.
Por exemplo, você poderia digitar:
ln -s /usr/local/bin/clisp ~/bin/lisp
Então,
uma vez no Emacs, digite M-x run-lisp
(G-x run-lisp). Você pode enviar código LISP para o
LISP que você acabou de invocar e fazer toda uma série de outras coisas. Para
maiores informações, digite
C-h m (Control-h
m) de qualquer buffer que esteja em modo-LISP.
Na
verdade, você nem precisa fazer o atalho (simblic
link) acima. Emacs possui uma variável chamada inferior-lisp-program; assim você só precisa adicionar a
linha:
(setq
inferior-lisp-program "/usr/local/bin/clisp")
ao seu arquivo .emacs, Emacs vai saber encontrar CLISP quando você digita M-x run-lisp.