Reinizializzazione corretta di un elenco? Cosa sta succedendo sotto il cofano?


8

Mi sto insegnando un po 'più elisp e ho riscontrato il seguente problema:

Se voglio ripristinare una variabile di elenco, questa non verrà aggiornata dopo la prima valutazione. Ecco un esempio di codice:

(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)

Sono interessato a due cose. Il primo è quello di saperne di più su ciò che sta accadendo "sotto il cofano", quindi perché funziona la prima volta e fallisce nelle chiamate successive?

La seconda e più pratica domanda è come reinizializzare correttamente l'elenco o c'è un altro modo comune di fare qualcosa del genere?

Una soluzione alternativa che ho riscontrato è utilizzare un elenco citato e valutare il contenuto in questo modo:

(setq example `(,3)) 

2
Riassunto: Non mi aspetto '(some list)di essere eqa '(some list)- mai .C'è generalmente alcuna garanzia in Lisp quel codice che cita visibilmente un elenco rendimenti nuova struttura della lista ogni volta. In alcune implementazioni Lisp potrebbe o potrebbe accadere qualche volta. In altri, non lo fa mai. Il codice non dovrebbe comunque dipendere da tali comportamenti dall'implementazione. Se si desidera una nuova struttura di elenco, utilizzare listo conso equivalente.
Disegnato il

(Immagino che questa domanda sia un duplicato, ma non so dove sia il duplicato.)
Disegnato il

1
Penso che il problema qui sia che examplenon è mai stato dichiarato come variabile, quindi setqdeve comportarsi come se dichiarasse una nuova variabile, ma in seguito quando si chiama di initializenuovo viene creata una nuova variabile, mentre modifyricorda quella vecchia ... in ogni caso non si tratta di un comportamento previsto, tuttavia l'uso di setqcon qualcosa che non è stato introdotto in precedenza come variabile potrebbe anche essere indefinito.
wvxvw,

3
OK, ho capito cosa succede. '(3)è trattato come un valore letterale, quindi una volta che (setcar '(3) 2), ogni volta che si fa (defvar foo '(3))o (let ((foo '(3)))e così via è probabile ottenere un valore di foopari a '(2). Dico "probabile" perché questo comportamento non è garantito, è una sorta di ottimizzazione che l'interprete fa ogni volta che lo desidera, qualcosa noto come eliminazione della sottoespressione delle costanti (un caso particolare di). Quindi, ciò che ha scritto abo-abo non è esattamente il motivo. È più come modificare una stringa letterale in C (che in genere genera un avviso).
wvxvw,

Risposte:


5

Forse questo chiarirà un po 'di confusione:

  • La tua funzione initilizenon inizializza la variabile example. Lo imposta su una particolare cella contro - la stessa cella contro ogni volta che viene chiamata. La prima volta che initilizeviene chiamato, viene setqassegnato examplea una nuova cella contro, che è il risultato della valutazione '(3). Chiamate successive per initilizeriassegnare examplealla stessa cella di contro.

  • Poiché initilizeriassegna semplicemente la stessa cella di contro example, modifyimposta semplicemente l'auto della stessa cella di contro 2ogni volta che viene chiamata.

  • Per inizializzare un elenco, utilizzare listo cons(o un sexp di backquote equivalente, come `(,(+ 2 1))o `(,3)). Ad esempio, utilizzare (list 3).

La chiave per comprenderlo è sapere che una cella di contro quotata viene valutata una sola volta e, successivamente, viene restituita la stessa cella di contro. Questo non è necessariamente il modo in cui si comportano tutti i Lisps, ma è come si comporta Emacs Lisp.

Più in generale, il comportamento della valutazione di un oggetto mutabile quotato dipende dall'implementazione, se non dalla lingua. In Common Lisp, ad esempio, sono abbastanza sicuro che non ci sia nulla nella definizione (spec) del linguaggio che definisce il comportamento in questo senso - è lasciato all'implementazione.

Riepilogo: non aspettatevi mai "(un elenco) da eq a" (un elenco), mai. In genere, in Lisp non esiste alcuna garanzia che il codice che cita visibilmente un elenco restituisca ogni volta una nuova struttura di elenco. In alcune implementazioni Lisp potrebbe o potrebbe accadere qualche volta. In altri, non lo fa mai. Il codice non dovrebbe comunque dipendere da tali comportamenti dall'implementazione. Se si desidera una nuova struttura di elenco, utilizzare listo conso equivalente.


1
Il tono condiscendente nelle prime due righe della tua risposta è necessario? Altrimenti una risposta illuminante.
asjo,

@asjo: L'intenzione non era di condannare in alcun modo; scusa. Spero sia più chiaro ora.
Estratto il

Grazie per chiarire un po 'le cose. Con il termine "argomento" intendevo l'argomento `(, 3) per la funzione setq, anche se capisco che è un po 'poco chiaro perché sto davvero valutando il 3 nell'elenco citato. Lo modificherò.
Clemera,

@Drew Great! È più facile da leggere ora.
asjo,

5

È possibile utilizzare (setq example (list 3))per evitare questo errore.

Ciò che accade è initcessionari un oggetto che inizialmente contiene (3)a example. Imposta il valore dell'oggetto solo una volta. Successivamente, si modifica questo valore.

Ecco il tuo esempio in C ++, se lo capisci meglio:

#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;
}

1
Grazie, non conosco C ++ ma capisco il tuo esempio. Una cosa mi disturba comunque: nell'esempio di codice introduci l'archiviazione variabile che crea l'oggetto con il valore iniziale solo se non è stato ancora impostato. Come si traduce in ciò che sta facendo l'interprete di Emacs Lisp? Voglio dire se rivaluto la initilizefunzione e la richiamo modifyverrà mostrata di (3)nuovo solo perché ho rivalutato la funzione.
Clemera,

1
Non riesco ad entrare nei dettagli (non li conosco esattamente), ma penso (3)a un oggetto temporaneo di cui fa parte init. initIl corpo si trova exampleall'indirizzo di quell'oggetto temporaneo, non ne tocca il valore.
abo-ABO
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.