Reinicialização adequada de uma lista? O que está acontecendo sob o capô?


8

Estou me ensinando um pouco mais sobre elisp e encontrei o seguinte problema:

Se eu quiser redefinir uma variável de lista, ela não será atualizada após a primeira avaliação. Aqui está um exemplo de código:

(defun initilize ()
  (setq example '(3)))

(defun modify ()
  (initilize)
  (message "%S" example)
  (setcar example 2))

; M-x eval-buffer RET
(modify) ; message --> (3)
(modify) ; message --> (2)
(modify) ; message --> (2)

Estou interessado em duas coisas. A primeira é aprender mais sobre o que está acontecendo "oculto", então por que funciona pela primeira vez e falha nas chamadas subseqüentes?

A segunda e mais prática questão é como reinicializar a lista corretamente ou existe outra maneira comum de fazer algo assim?

Uma solução alternativa que encontrei é usar uma lista citada e avaliar o conteúdo assim:

(setq example `(,3)) 

2
Resumo: Não espere '(some list)para ser eqa '(some list)- sempre .Não é geralmente nenhuma garantia em Lisp que o código que visivelmente cita uma lista de retornos nova estrutura de lista de cada vez. Em algumas implementações do Lisp, pode ser, ou pode ser por algumas vezes. Em outros, isso nunca acontece. De qualquer forma, seu código não deve depender de nenhum comportamento da implementação. Se você deseja uma nova estrutura de lista, use listou consou equivalente.
Drew

(Eu estou supondo que esta questão é uma duplicata, mas eu não sei onde o duplicado é.)
de Drew

11
Eu acho que o problema aqui é que examplenunca foi declarado como uma variável, então setqdeve estar agindo como se declarasse uma nova variável, mas mais tarde, quando você ligar initializenovamente, uma nova variável está sendo criada, enquanto se modifylembra da antiga ... em qualquer caso, esse não é um comportamento esperado; no entanto, o uso de setqalgo que não foi introduzido anteriormente como uma variável também pode ser indefinido.
Wvxvw 27/11/2015

3
OK, eu descobri o que acontece. '(3)é tratado como um valor literal, uma vez que você (setcar '(3) 2), sempre que você fizer (defvar foo '(3))ou (let ((foo '(3)))e assim por diante você provavelmente irá obter um valor de fooigual '(2). Eu digo "provável" porque esse comportamento não é garantido, é um tipo de otimização que o intérprete faz sempre que parece, algo conhecido como eliminação de subexpressão de constantes (um caso específico de). Então, o que abo-abo escreveu não é exatamente o motivo. É mais como modificar uma string literal em C (que geralmente gera um aviso).
wvxvw

Respostas:


5

Talvez isso esclareça parte da confusão:

  • Sua função initilizenão inicializa variável example. Ele o define como uma célula de contras específica - a mesma célula de contras cada vez que é chamada. Na primeira vez que initilizeé chamado, ele setqatribui examplea uma nova célula de contras, que é o resultado da avaliação '(3). Chamadas subsequentes para initilizereatribuir exampleà mesma célula de contras.

  • Como initilizeapenas reatribui a mesma célula de contras example, modifyapenas define o carro dessa mesma célula de contras a 2cada vez que é chamada.

  • Para inicializar uma lista, use listou cons(ou um sexp equivalente de uma cota posterior, como `(,(+ 2 1))ou `(,3)). Por exemplo, use (list 3).

A chave para entender isso é saber que uma célula de contras citada é avaliada apenas uma vez e, em seguida, a mesma célula de contras é retornada. Não é necessariamente assim que todos os Lisps se comportam, mas é como o Emacs Lisp se comporta.

De um modo mais geral, o comportamento de avaliar um objeto mutável citado depende da implementação, se não da linguagem. No Common Lisp, por exemplo, tenho certeza de que não há nada na definição (especificação) da linguagem que defina o comportamento a esse respeito - isso fica por conta da implementação.

Resumo: Não espere que '(alguma lista) seja igual a' (alguma lista) - sempre. Geralmente, não há garantia no Lisp de que o código que cita visivelmente uma lista retorne sempre uma nova estrutura de lista. Em algumas implementações do Lisp, pode ser, ou pode ser por algumas vezes. Em outros, isso nunca acontece. De qualquer forma, seu código não deve depender de nenhum comportamento da implementação. Se você deseja uma nova estrutura de lista, use listou consou equivalente.


11
O tom condescendente nas duas primeiras linhas da sua resposta é necessário? Caso contrário, uma resposta esclarecedora.
Asjo

@asjo: A intenção não era condescender de forma alguma; desculpa. Espero que esteja mais claro agora.
Tirou

Graças a esclarecer um pouco as coisas. Com o termo "argumento", eu quis dizer o argumento `(, 3) para a função setq, embora eu entenda que é um pouco incerto, porque estou realmente avaliando os 3 na lista citada. Eu vou editar isso.
Clemera

@Drew Great! É mais amigável ler agora.
Asjo

5

Você pode usar (setq example (list 3))para evitar esse erro.

O que acontece é initatribui um objeto que inicialmente contém (3)a example. Ele define o valor do objeto apenas uma vez. Posteriormente, você modifica esse valor.

Aqui está o seu exemplo em C ++, se você entender isso melhor:

#include <stdio.h>
#include <string.h>
char* example;
char* storage = 0;
char* create_object_once (const char* str) {
  if (storage == 0) {
    storage = new char[strlen (str)];
    strcpy (storage, str);
  }
  return storage;
}
void init () {
  example = create_object_once ("test");
}
void modify () {
  init ();
  printf ("%s\n", example);
  example[0] = 'f';
}
int main (int argc, char *argv[]) {
  modify ();
  modify ();
  modify ();
  return 0;
}

11
Obrigado, eu não sei C ++, mas eu entendo o seu exemplo. Uma coisa ainda me incomoda: no seu exemplo de código, você introduz a variável storage que cria o objeto com o valor inicial apenas se ainda não tiver sido definido. Como isso se traduz no que o intérprete do Emacs Lisp está fazendo? Quero dizer, se eu reavaliar a initilizefunção e ligar modifynovamente, ela será exibida (3)novamente apenas porque reavaliei a função.
Clemera

11
Não consigo entrar em detalhes (não os conheço exatamente), mas penso (3)que é um objeto temporário do qual faz parte init. initO corpo do conjunto exampleé o endereço desse objeto temporário, ele não toca em seu valor.
abo-abo
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.