Domanda

Sto leggendo il libro di Graham "On Lisp" e non riesco a capire il seguente esempio a pagina 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)))

Qualcuno capisce cosa sta succedendo qui?Questo sta seriamente rovinando il mio modello mentale di ciò che fa la citazione.

È stato utile?

Soluzione

nconc è un'operazione distruttiva che altera il primo argomento cambiando la sua coda. In questo caso, significa che l'elenco costante '(oh my) ottiene una nuova coda.

Per spera rendere questo più chiaro. E 'un po' come questo:

; 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

La sostituzione '(oh my) con correzioni (list 'oh 'my) questo perché ci non è più una costante di essere condivisa da tutti indistintamente. Ogni chiamata a exclaim genera una nuova lista (scopo della funzione list nella vita è quello di creare nuove liste).

Altri suggerimenti

L'osservazione che il tuo modello mentale di citazione potrebbe essere difettoso è eccellente, anche se può applicarsi o meno a seconda di quale sia quel modello mentale.

Innanzitutto, ricorda che ci sono varie fasi nell'esecuzione del programma.Un ambiente Lisp deve prima Leggere il testo del programma in strutture dati (elenchi, simboli e vari dati letterali come stringhe e numeri).Successivamente, può o no compilare quelle strutture dati in codice macchina o in una sorta di formato intermedio.Infine, il codice risultante è valutato (nel caso del codice macchina, ovviamente, ciò può semplicemente significare saltare all'indirizzo appropriato).

Mettiamo da parte per ora il problema della compilazione e concentriamoci sulle fasi di lettura e valutazione, assumendo (per semplicità) che l'input del valutatore sia l'elenco delle strutture dati lette dal lettore.

Consideriamo un modulo (QUOTE x) Dove X è una rappresentazione testuale di un oggetto.Questo potrebbe essere un simbolo letterale come in (QUOTE ABC), un elenco letterale come in (QUOTE (A B C)), una stringa letterale come in (QUOTE "abc"), o qualsiasi altro tipo di letterale.Nella fase di lettura, il lettore leggerà il modulo come una lista (chiamiamola modulo1) il cui primo elemento è il simbolo QUOTE e il cui secondo elemento è l'oggetto X' la cui rappresentazione testuale è X.Nota che sto specificatamente dicendo che l'oggetto X' è memorizzato all'interno dell'elenco che rappresenta l'espressione, cioè.in un certo senso, è memorizzato come una parte del codice stesso.

Ora tocca al valutatore.Il contributo del valutatore è modulo1, che è un elenco.Quindi esamina il primo elemento di modulo1, e, avendo stabilito che si tratta del simbolo QUOTE, restituisce come risultato della valutazione il secondo elemento della lista.Questo è il punto cruciale.Il valutatore restituisce il secondo elemento della lista da valutare, ovvero ciò che il lettore ha letto nella prima fase di esecuzione (prima della compilazione!). Questo è tutto ciò che fa. Non c'è magia, è molto semplice e, significativamente, non vengono creati nuovi oggetti e non vengono copiati quelli esistenti.

Pertanto, ogni volta che modifichi un "elenco tra virgolette", stai modificando il codice stesso.Il codice automodificante è una cosa molto confusa e, in questo caso, il comportamento è effettivamente indefinito (perché ANSI Common Lisp consente alle implementazioni di inserire il codice nella memoria di sola lettura).

Naturalmente, quanto sopra è semplicemente un modello mentale.Le implementazioni sono libere di implementare il modello in vari modi e, in effetti, non conosco alcuna implementazione di Common Lisp che, come la mia spiegazione, non esegua alcuna compilazione.Eppure questa è l’idea di base.

In Common Lisp.

Ricorda:

'(1 2 3 4)

Sopra è un Elenco letterale . dati costanti .

(list 1 2 3 4)

LIST è una funzione che, quando chiamata restituisce un fresco nuovo elenco con i suoi argomenti come elementi della lista.

evitare di modificare le liste letterali. Gli effetti non sono standardizzati. Immaginate un Lisp che raccoglie tutti i dati costanti in una scrittura unica area di memoria. Immaginate un Lisp che prende liste fisso e azioni tra le varie funzioni.

(defun a () '(1 2 3)

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

Un Lisp compilatore può creare una lista che è condivisa da entrambe le funzioni.

Se si modifica l'elenco restituito dalla funzione a

  • potrebbe non essere modificato
  • potrebbe essere cambiato
  • potrebbe essere un errore
  • potrebbe anche modificare l'elenco restituito dalla funzione b

Le implementazioni hanno la libertà di fare ciò che vogliono. Questo lascia spazio per le ottimizzazioni.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top