Pergunta

Estou trabalhando no livro de Graham "On Lisp" e não consigo entender o exemplo a seguir na página 37:

If we define exclaim so that its return value
incorporates a quoted list,

(defun exclaim (expression)
  (append expression ’(oh my)))

>  (exclaim ’(lions and tigers and bears))
(LIONS AND TIGERS AND BEARS OH MY)
> (nconc * ’(goodness))
(LIONS AND TIGERS AND BEARS OH MY GOODNESS)

could alter the list within the function:

> (exclaim ’(fixnums and bignums and floats))
(FIXNUMS AND BIGNUMS AND FLOATS OH MY GOODNESS)

To make exclaim proof against such problems, it should be written:
(defun exclaim (expression)
  (append expression (list ’oh ’my)))

Alguém entende o que está acontecendo aqui? Isso está seriamente ferrando com o meu modelo mental do que a citação faz.

Foi útil?

Solução

nconc é uma operação destrutiva que altera seu primeiro argumento, mudando sua cauda. Nesse caso, significa que a lista constante '(oh my) recebe uma nova cauda.

Esperamos deixar isso mais claro. É um pouco assim:

; Hidden variable inside exclaim
oh_my = oh → my → nil

(exclaim '(lions and tigers and bears)) =
    lions → and → tigers → and → bears → oh_my

(nconc * '(goodness)) destructively appends goodness to the last result:
    lions → and → tigers → and → bears → oh → my → goodness → nil
so now, oh_my = oh → my → goodness → nil

Substituindo '(oh my) com (list 'oh 'my) Corrige isso porque não há mais uma constante sendo compartilhada por todos e diversos. Cada chamada para exclaim gera uma nova lista (o list O objetivo da função na vida é criar novas listas).

Outras dicas

A observação de que seu modelo mental de citar pode ser falho é excelente - embora possa ou não se aplicar, dependendo do que esse modelo mental é.

Primeiro, lembre -se de que existem vários estágios para programar a execução. Um ambiente lisp deve primeiro ler O programa de texto em estruturas de dados (listas, símbolos e vários dados literais, como strings e números). Em seguida, pode ou não compilar Essas estruturas de dados no código da máquina ou algum tipo de formato intermediário. Finalmente, o código resultante é avaliado (No caso do código da máquina, é claro, isso pode simplesmente significar pular para o endereço apropriado).

Vamos deixar de lado a questão da compilação por enquanto e focar nos estágios de leitura e avaliação, assumindo (por simplicidade) que a entrada do avaliador seja a lista de estruturas de dados lidas pelo leitor.

Considere um formulário (QUOTE x) Onde x é alguma representação textual de um objeto. Isso pode ser símbolo literal como em (QUOTE ABC), uma lista literal como em (QUOTE (A B C)), uma corda literal como em (QUOTE "abc"), ou qualquer outro tipo de literal. Na fase de leitura, o leitor lerá o formulário como uma lista (ligue para formulário 1) cujo primeiro elemento é o símbolo QUOTE e cujo segundo elemento é o objeto x ' cuja representação textual é x. Observe que estou dizendo especificamente que o objeto x ' está armazenado dentro da lista que representa a expressão, ou seja, em certo sentido, é armazenado como uma parte do próprio código.

Agora é a vez do avaliador. A entrada do avaliador é formulário 1, que é uma lista. Então ele olha para o primeiro elemento de formulário 1, e, tendo determinado que é o símbolo QUOTE, ele retorna como resultado da avaliação o segundo elemento da lista. Este é o ponto crucial. O avaliador retorna o segundo elemento da lista a ser avaliado, que é o que o leitor leu no primeiro estágio de execução (antes da compilação!). Isso é tudo o que faz. Não há mágica, é muito simples e, significativamente, nenhum novo objetivo é criado e nenhum existente é copiado.

Portanto, sempre que você modifica uma "lista citada", você está modificando o próprio código. O código auto-modificador é uma coisa muito confusa e, nesse caso, o comportamento é realmente indefinido (porque o ANSI Common Lisp permite que as implementações coloquem código na memória somente leitura).

Obviamente, o exposto acima é apenas um modelo mental. As implementações são gratuitas para implementar o modelo de várias maneiras e, de fato, não conheço nenhuma implementação do Lisp comum que, como minha explicação, não faça compilação. Ainda assim, essa é a ideia básica.

Em lisp comum.

Lembrar:

'(1 2 3 4)

Acima está a Lista literal. Dados constantes.

(list 1 2 3 4)

Lista é uma função que quando a chamada retorna um nova lista com seus argumentos como elementos de lista.

Evite modificar listas literais. Os efeitos não são padronizados. Imagine um Lisp que compila todos os dados constantes em uma área de memória de gravação. Imagine um Lisp que leva listas constantes e as compartilha entre as funções.

(defun a () '(1 2 3)

(defun b () '(1 2 3))

Um compilador LISP pode criar uma lista compartilhada pelas duas funções.

Se você modificar a lista retornada pela função A

  • Pode não ser alterado
  • Pode ser alterado
  • Pode ser um erro
  • Também pode alterar a lista retornada pela função B

As implementações têm a liberdade de fazer o que gostam. Isso deixa espaço para otimizações.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top