Aula 20 Lisp

https://lisp-lang.org ou https://common-lisp.net/downloads (common lisp)

https://common-lisp.net/downloads lisp online

https://clojure.org/index Clojure

Lisp é facilmente modificável e acaba incorporando ideias novas em programação

Listas

Entre pareteses, heterogenea, separa por brancos

(5  6  9.8 abobora () (5 6)  "abc")

abobora é um simbolo/atomo, como em prolog.

Sintaxe

(funcao arg1  arg2 ...)

(operador arg1 arg2 ...)

(special-form arg1 arg2 ...)
(+ 4 5 6 (cos 4))

(if (> x 0) (* x 4) (- x 2))

Diferença de função e comando (special form)

Em outras linguagens de programação uma diferença é a sintaxe

 if (a<b)  a else b 

 if(a<b,a,b)
 

mas em lisp nao há diferença sintática

     (if (> a b) a b)
          
     (func (> a b) a b)

A diferença central é que funções sempre avaliam seus argumentos e comandos podem nao avaliar todos

(if (> a b) a (/ 1 0))

let

versão simples

(let ()
    exp1
    exp2
    ...
    expn)

avalia cada expressão na ordem. Retorna o valor da ultima expressão

versão com variáveis locais

(let ((var1 val1)
      (var2 val2)
      ...
      )
    exp1
    exp2
    ...
    expn)

cria as variáveis locais var1 … e atribui os valores val1 … (como no haskell)

setf

(setf x exp) avalia a expressão e atribui a variável x (local ou global)

defun

define funções

(defun double (x)
    (* x 2)
    )
    
(defun aux (x y)
    (let ((a (+ x y))
          b
          )
     ...
    )
)

listas (funcoes)

class X
 pass
 
a = X()
a.nome = "jose" 
a.dado = a

b = X()
b.nome = "jose" 
b.dado = b

a==b

Exemplos de programas

(defun reverte (lista)
    (if (null lista) NIL
        (append (reverte (rest lista)) 
                (list (first lista)))
        ))
(defun revacc (lista acc)
    (if (null lista) acc
        (revacc (rest lista) 
                (cons (first lista) acc))
        ))
(defun trocatodos (novo velho lista)
    (if (null lista) nil
        (if (eql (first lista) velho) 
            (cons novo (trocatodos novo velho (rest lista)))
            (cons (first lista) (trocatodos novo velho (rest lista)))
        )
    )
)

ou

(defun trocatodos (novo velho lista)
    (if (null lista) nil
        (let ((a (first lista))
              (r (rest lista))
             )
           (if (eql a velho) 
               (cons novo (trocatodos novo velho r))
               (cons a (trocatodos novo velho r))
           )
       )
    )
)

ou ainda

(defun trocatodos (novo velho lista)
    (if (null lista) nil
        (let* ((a (first lista))       <---- let*
               (r (rest lista))
               (resto (trocatodos novo velho r))
             )
           (if (eql a velho) 
               (cons novo resto)
               (cons a resto))
           )))
     

Exercicios

os mesmos da 1a aula do haskell (e do prolog)

(defun separa (item lista)
    (if (null lista) (list nil nil)
        (if (eql item (first lista)) 
            (list nil (rest lista))
            (let* ((x (separa item (rest lista)))
                   (y (first x))
                   (z (rest x))
                  )
                  (cons (cons (first lista) y) z)
              ))))

quote

(1 2 3) não funciona, 1 não é uma função/operador/forma especial

listas são avaliadas como expressões.

eu preciso desligar o avaliador para criar a lista (1 2 3)

(quote (1 2 3))

'(1 2 3)

ou

(list 1 2 3)
(cons 1 (cons 2 (cons 3 nil)))

avaliação

Como (list 1 2 (+ 4 5)) avalia?

Outras coisas além de números avaliam para si mesmos

A avaliação de um simbolo retorna o valor armazenado (via let ou setf) no simbolo/variável

(setf x (+ 1 2))
x

(let ((x 99.7))
    (print x))

quote simbolo retorna o simbolo: 'abc retorna o simbolo abc

Static & Dynamic scope

variavies livres em funções.

(setf x 8)
(defun f1 (a) (+ x a))
   
(let ((x 100)) 
        (f1 4))
        
==> 12

em python

x = 8
def f1 (a):
    return x+a
    
def f2():
    x = 100
    f1(4)

f2()
==> 12

que é o comum em outras linguagens de programação. A variavel “livre” x de f1 vem do contexto sintatico onde a definição de f1 esta. Nesse caso a variavel global.

Isto é o escopo estatico

Lisp permite o escopo dinamico

(defvar x 8)   <--- declara um escopo dinamico para x
(defun f1 (a) 
    (+ x a))

(let ((x 100)) (f1 4))

==> 104

no segundo caso, o escopo de x é dinamico, e o valor usado será o último disponível na pilha de execuçao.

como construir um programa montando uma lista

(list 'if '(> x 0) (cons '* '(x 4))
      (list '- 'x 2))
      
==>       
      
(if (> x 0) (* x 4) (- x 2))      
      

isso é o central na metaprogramação

macros/metaprogramming

“funções” que nao avaliam seus argumentos, mas geram uma lista usando esses argumentos, que é uma expressão Lisp. Essa lista gerada é avaliada ou compilada

(when teste a1 a2 .. an) 

===> converte para

(if teste (let ()
           a1 a2 .. an))
(defmacro when2 (teste &rest resto)
    (list 'if teste (cons 'let (cons () resto)))
    )

(macroexpand '(when2 (pos a) (incf a) (print a)))
(pyif  teste t1 t2 t3 else: e1 e2)

==> converte para 

(if teste (let () t1 t2 t3) (let () e1 e2))

(defmacro pyif (teste &rest resto)
    (let* ((troca (separa `else: resto))
           (pthen  (first troca))
           (pelse  (second troca))
           (xthen (if (null pthen) nil
                      (cons 'let (cons nil pthen))))
           (xelse (if (null pelse) nil
                      (cons 'let (cons nil pelse))))
          )
       (list 'if teste xthen xelse)
       ))
           

read macros

define comportamentos na leitura para caracteres especias

'( a b c) ==> (quote (a b c))

quote é uma forma especial que nao avalia seu argumento e retorna ele

(defmacro when2 (teste &rest resto)
    `(if ,teste 
         (let () ,@resto)
         )
)

lisp-1 vs lisp-2

Se variáveis podem ter tanto um valor e uma função associada (lisp-2) ou apenas um valor (que pode ser uma função) (lisp-1)

python

soma1 = 6

def soma1 (a):
    return a*2

x só pode ter um valor (e vira uma função no def)

common lisp

(setf soma1 6)
(defun soma1 (a) (+ 1 a))

common lisp é um lisp-2

scheme é um lisp-1

O problema de um lisp-2 sao as funções de alto nível

(map soma1 '(1 2 3 4))

(map #'soma1 '(1 2 3))

Clojure

A mais importante é listas que não vão ser avaliadas (em formas especiais) são trocadas por [ ]

(defun soma (x y)
   (let ((soma (+ x y))
        )
    soma
    ))
    
(defn soma [x y]
   (let [soma (+ x y)]
    soma))

Implementação de lisp

uma implementação inicial de lisp é fácil.

processamento sinatico é facil - expressões estão entre ( e ) casados

implemente (no baixo nível) apenas alguns primitivos

Por exemplo https://homes.cs.aau.dk/~normark/prog3-03/html/notes/languages_themes-list-in-lisp.html

https://stackoverflow.com/questions/2664618/what-does-it-mean-that-lisp-can-be-written-in-itself

https://github.com/fluentpython/lispy/tree/main

As outras coisas são macros do próprio lisp

Meta-circular evaluator