(ver FundComp, FCBibliografia)
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
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.
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)
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.