Scheme Versus Lisp

(ver FundComp, FCBibliografia)

Seção 1.3:

Scheme: "É importante ressaltar que, dada uma expressão, não existe uma ordem fixa para o seu traço."

Lisp: A ordem de avaliação é pré-definida. Se f não é uma forma especial então o traço de (f expr1 expr2 expr3 expr4) é

    (f expr1 expr2 expr3 expr4)
--> (f rslt1 expr2 expr3 expr4)
--> (f rslt1 rslt2 expr3 expr4)
--> (f rslt1 rslt2 rslt3 expr4)
--> (f rslt1 rslt2 rslt3 rslt4)
--> rsltf

Seção 1.4:

A apostila está chamando de "ambiente" o que nós chamamos de "contexto".

O Scheme usa "define" pra atribuir valores a variáveis; o Lisp usa "setq".

Scheme: (define arco (+ (* aro 3) 5))
Lisp: (setq arco (+ (* aro 3) 5))

A apostila diz: "Quando executamos um "let", primeiro todas as expressões "exp_i" são calculadas, e associadas aos respectivos nomes, criando um ambiente local. Em seguida, o corpo é calculado, trocando-se cada ocorrência de "nome_i" pelo valor correspondente no ambiente"...

Isso é uma simplificação do que acontece de verdade. Em Scheme, só as ocorrências de nome_i como variável livre são substituídas (ver VariaveisLivres). Em Lisp, toda vez que o sistema precisa do valor da variável nome_i ele procura primeiro no contexto mais próximo, depois no contexto seguinte, etc, etc, até chegar ao contexto global.

Se executamos

(setq a 1)
(let ((b 2)
      (c 3))
  (let ((c 4)
        (d 5))
    (* d c b a)))

a expressão (* d c b a) vai ser avaliada dentro de três contextos:

(a |-> 1)   contexto global (mais externo)
(b |-> 2
 c |-> 3)   contexto criado pelo primeiro let
(c |-> 4
 d |-> 5)   contexto criado pelo segundo let (mais interno, mais próximo do *)
Os valores de d e de c vão ser os que estão definidos no contexto mais
interno: d |-> 5, c |->4; o valor de b vai ser o do contexto do
primeiro let, b |-> 2; e o valor de a vai ser o "valor global" de a,
a |-> 1. O resultado é (* 5 4 2 1) |-> 40.

Sobre o exemplo 1.4: o Lisp tem uma forma especial "let*" que é variação do "let", e funciona como uma série de "let"s aninhados.

Seção 1.5:

Como o Emacs é um editor de texto a noção de "entrada e saída" dele (em inglês: "input" e "output", ou "I/O") é totalmente diferente da noção do Scheme. Uma tradução aproximada:

Scheme: (display "Alô mundo")
Emacs: (insert "Alô mundo")

E "read" em Lisp é uma outra coisa: em Lisp a expressão (read STRING) tenta interpretar STRING como um objeto de Lisp escrito textualmente:

(read "555")
   |-> 555
(read "simbolo")
   |-> simbolo
(read "(a   (b c)d")
   |-> (a (b c) d)
(read "(a \"bcd\" e)")
   |-> (a "bcd" e)
(read "(a b")
   |-> ERRO

Mais alguns exemplos:

(read "()")
   |-> nil
(read "(a . (b c))")
   |-> (a b c)
(read "(a . (b . nil))")
   |-> (a b)

Seção 1.7:

Scheme: (define f (lambda (x) (* 2 x)))
Lisp: (defun f (x) (* 2 x))

Scheme: (define (f x) (* 2 x))
Lisp: (defun f (x) (* 2 x))

Em Lisp cada símbolo pode guardar dois valores separados: o valor dele "como variável" e o valor dele "como função". Por exemplo:

(setq quadrado "aha!")
(defun quadrado (x) (* x x))

quadrado
  |-> "aha!"
(quadrado 5)
  |-> 25

Obs: É possível ler e mudar o valor "como função" de um símbolo:

(defun quadrado (x) (* x x))
  |-> quadrado
(symbol-function 'quadrado)
  |-> (lambda (x) (* x x))

(fset 'quadrado (lambda (x) (* x x x )))
  |-> (lambda (x) (* x x x ))
(quadrado 3)
  |-> 27

Porquê esse "'" em (fset 'quadrado ...)? Compare essa função "fset" com a função "set", que é uma variação do "setq":

(setq a 2)
  |-> 2
(setq b 3)
  |-> 3

'2
  |-> a
(set 'a 4)
  |-> 4
a
  |-> 4

Repare: o setq é uma forma especial - ele não avalia o primeiro parâmetro! O "set" é mais "normal" que o setq: (set expr1 expr2) avalia expr1 e expr2, e obtém rslt1 e rslt2; aí ele verifica que rslt1 e' um símbolo - se não for ele dá um erro - e aí ele faz com que o valor "como variável" do símbolo rslt1 passe a ser rslt2.