2013-06-04 24 views
9

Questo è un seguito al mio previous question. Non sono convinto che il codice Lisp sia Homoiconic come codice macchina su un'architettura Von Neumann. Mi sembra ovvio che in entrambi i casi il codice sia rappresentato come dati, ma sembra anche evidente che è possibile sfruttare questa proprietà molto più liberamente nel codice macchina di quanto non sia possibile in Lisp.Livelli di omosessualità

Quando si digerisce con il codice macchina, il codice di auto modifica è così facile che accade sempre, spesso per caso e con (nella mia esperienza) risultati esilaranti. Durante la scrittura di un semplice programma "stampa il numero 0-15", potrei avere un errore "uno alla volta" con uno dei miei suggerimenti. Finirò per scaricare accidentalmente tutto ciò che è nel Registro 1 nell'indirizzo in memoria che contiene l'istruzione successiva, e al suo posto viene eseguita un'istruzione casuale. (Sempre fantastico quando si tratta di una sorta di "goto". Dio sa dove finirà e cosa farà dopo).

Non c'è davvero alcuna separazione tra codice e dati. Tutto è simultaneamente un'istruzione (anche se è solo un NOP), un puntatore e un semplice vecchio numero. Ed è possibile che il codice cambi sotto i tuoi occhi.

Per favore aiutatemi con uno scenario Lisp Mi sono grattato la testa. Dire che ho ottenuto il seguente programma:

(defun factorial (n) 
    (if (<= n 1) 
     1 
     (* n (factorial (- n 1))))) 
; -- Demonstrate the output of factorial -- 
; -- The part that does the Self modifying goes here – 
; -- Demonstrate the changed output of factorial 

Ora quello che voglio che accada è di aggiungere a questo programma un codice Lisp che cambierà il * ad un +, modificare la < = ad un> =, bastone a (+ 1 2 3) da qualche parte lì dentro, e in generale infastidisce la funzione. E poi voglio che il programma esegua il pasticcio assoluto che ne risulta.

Punto chiave: a meno che non si sia verificato un errore irreversibile nel codice di esempio, è possibile modificare solo la parte -– More code goes here –-. Quello che vedi sopra è il codice. Non voglio che tu citi l'intera lista e la memorizzi in una variabile in modo che possa essere manipolata e sputata come una funzione separata con lo stesso nome; Non voglio una ridefinizione standard di fattoriale come qualcosa di completamente diverso. Voglio quel codice, proprio lì che posso vedere sul mio schermo per cambiare se stesso davanti ai miei occhi, proprio come il codice macchina.

Se questa è una richiesta impossibile/irragionevole, allora consolida ulteriormente nella mia mente l'idea che l'omoiconicità non è una proprietà discreta che un linguaggio ha o non ha, è uno spettro e Lisp non lo è il bordo sanguinante. (In alternativa, il Lisp è come Homoiconic come vengono e sto cercando qualche altro termine per descrivere l'auto-modifica di codice macchina)

+0

inoltre, inb4 commenta che il codice sul mio schermo è improbabile che cambi di fronte ai miei occhi perché è solo un testo su una pagina web. Se sinceramente non capisco cosa intendo e non sto trollando cercherò di chiarire un po 'di più :) – TheIronKnuckle

+0

Il codice macchina tipico non è "omoiconico" e il linguaggio assembly meno. Sebbene il codice macchina sia un array di bit, i linguaggi macchina non hanno gli strumenti per manipolare specificamente matrici di bit che rappresentano programmi di linguaggio macchina. Ad esempio, data una serie di bit, non sai nemmeno dove, diciamo, inizia la diciassettesima istruzione, a meno che non siano tutti della stessa lunghezza. Se inserisci un codice in un modello, le correzioni di diramazione che attraversano il punto di inserimento devono essere ricalcolate e corrette. Hai bisogno di una grande libreria di routine per fare qualsiasi cosa, e non è inclusa nel linguaggio della macchina. – Kaz

+0

I linguaggi delle macchine ** hanno effettivamente ** gli strumenti per manipolare in modo specifico matrici di bit che rappresentano programmi di linguaggio macchina. Ogni volta che usi un'istruzione "store" è esattamente quello che sta succedendo. – TheIronKnuckle

risposta

15

È facile. Hai solo bisogno di cambiare la rappresentazione della lista. Tutto ciò che serve è un interprete Lisp .

L'implementazione Common Lisp LispWorks ci fornisce un interprete Lisp:

CL-USER 137 > (defun factorial (n) 
       (if (<= n 1) 
        1 
        (* n (factorial (- n 1))))) 
FACTORIAL 

CL-USER 138 > (fifth (function-lambda-expression #'factorial)) 
(IF (<= N 1) 1 (* N (FACTORIAL (- N 1)))) 

CL-USER 139 > (fourth (fifth (function-lambda-expression #'factorial))) 
(* N (FACTORIAL (- N 1))) 

CL-USER 140 > (setf (first (fourth (fifth (function-lambda-expression 
              #'factorial)))) 
        '+) 
+ 

CL-USER 141 > (fourth (fifth (function-lambda-expression #'factorial))) 
(+ N (FACTORIAL (- N 1))) 

CL-USER 142 > (factorial 10) 
55 

CL-USER 143 > (setf (first (fourth (fifth (function-lambda-expression 
              #'factorial)))) 
        '*) 
* 

CL-USER 144 > (factorial 10) 
3628800 

Ecco un esempio in cui una funzione si modifica. Per renderlo leggermente più semplice, utilizzo una funzionalità di Common Lisp: mi consente di scrivere codice che non è solo un elenco annidato, ma un grafico. In questo caso la funzione può accedere il proprio codice:

CL-USER 180 > (defun factorial (n) 
       (if (<= n 1) 
        1 
        (progn 
        (setf (first '#1=(* n (factorial (- n 1)))) 
          (case (first '#1#) 
          (+ '*) 
          (* '+))) 
        #1#))) 
FACTORIAL 

Sopra funzione utilizza alternativamente + o * modificando il codice.

#1= è un'etichetta nell'espressione, #1# quindi fa riferimento a tale etichetta.

CL-USER 181 > (factorial 10) 
4555 

In precedenti volte ('70/'80) in alcuni gruppi Lisp gli sviluppatori non sono state utilizzando un editor di testo per scrivere codice Lisp, ma un editor di struttura. I comandi dell'editor stavano cambiando direttamente la struttura del codice Lisp.

+1

haha ​​wow. È fantastico. Stavo cercando qualcosa del genere per yonks. E immagino che non ci sia nulla che ti impedisca di fare questo genere di cose al di fuori di un REPL (Quindi potresti potenzialmente avere una funzione ricorsiva che si modifica da sola?) – TheIronKnuckle

+0

Si noti che questo non è portatile Common Lisp, come FUNCTION- LAMBDA-EXPRESSION è autorizzata a restituire NIL. –

+2

@ThomasBartscher: vero. Suppongo anche che sia un interprete - anche qualcosa che non è portabile. Common Lisp non riguarda solo il linguaggio portatile, ma anche i casi in cui le implementazioni individuali hanno un certo grado di libertà (interprete vs compilatore, sviluppo vs consegna, ottimizzazioni, garbage collection, ...). Basta scegliere un'implementazione Common Lisp che abbia le giuste caratteristiche. –