2010-03-29 13 views
6

Abbiamo usato il Lisp nel mio corso di intelligenza artificiale. I compiti che ho ricevuto riguardavano la ricerca e la generazione di strutture ad albero. Per ogni incarico, ho finito per scrivere qualcosa di simile:Come posso SETF un elemento in un albero da un accessor?

(defun initial-state() 
    (list 
    0 ; score 
    nil ; children 
    0 ; value 
    0)) ; something else 

e costruire le mie funzioni intorno a questi "stati", che sono in realtà solo annidati liste con qualche struttura vagamente definita.

Per rendere la struttura più rigida, ho provato a scrivere di accesso, come ad esempio:

(defun state-score (state) 
    (nth 2 state)) 

Questo funziona per la lettura del valore (che dovrebbe essere tutto quello che ho bisogno di fare in un mondo ben funzionale. Tuttavia, mentre il tempo scricchiola, e comincio a scribacchiare follemente, a volte voglio una struttura mutevole). Non mi sembra di essere in grado di SETF la cosa restituita ... (luogo? Valore? Puntatore?).

ho un errore con qualcosa di simile:

(setf (state-score *state*) 10) 

A volte mi sembra di avere un po 'più di fortuna scrivendo la funzione di accesso/mutator come macro:

(defmacro state-score (state) 
    `(nth 2 ,state)) 

Tuttavia non so perché dovrebbe essere una macro, quindi non dovrei scriverlo come macro (eccetto che a volte funziona. Programmare per coincidenza è male).

Qual è una strategia appropriata per costruire tali strutture?

Ancora più importante, dove posso sapere di cosa sta succedendo qui (quali operazioni influenzano la memoria in che modo)?

risposta

13

Usa CLOS per strutture di dati

Il modo migliore per uscire questo è quello di imparare velocemente le basi di Clos.

(defclass state() 
    ((score :accessor state-score :initform 0) 
    (children :accessor state-children :initform nil) 
    (value :accessor state-value :initform 0))) 

(defun make-initial-state() 
    (make-instance 'state)) 

(defparameter *state* (make-initial-state)) 

(setf (state-score *state*) 10) 

Per la maggior parte del codice dell'applicazione strutture Evita

Per la maggior parte delle strutture di codice evitare - usarli solo quando ne avete bisogno e sai perché. Utilizzare invece le classi CLOS.

defstruct funziona anche con le liste

Se davvero si vuole utilizzare gli elenchi, una possibilità è quella di utilizzare la macro defstruct con le liste e farlo definire tutte le funzioni:

(defstruct (state (:type list)) 
    (score 0) 
    (children nil) 
    (value 0)) 

Sopra, l'opzione: type dice a DEFSTRUCT di usare una lista invece di una struttura.

? (defparameter *state* (make-state)) 
*STATE* 
? (setf (state-score *state*) 10) 
10 

(make-stato) restituisce una lista di tre elementi.

Possiamo scrivere funzioni setter

Se si vuole scrivere il codice a mano, quindi è possibile scrivere funzioni setter:

(defun get-my-score (state) 
    (first state)) 

(defun (setf get-my-score) (score state) 
    (setf (first state) score)) 

qui sopra definisce una funzione SETF. Il nome della funzione è in realtà una lista. I parametri per questa funzione devono essere prima il nuovo valore e quindi la cosa da impostare.

? (setf *state* (list 0 nil 0)) 
(0 NIL 0) 
? (setf (get-my-score *state*) 10) 
10 
? *state* 
(10 NIL 0) 

Il Common Lisp HyperSpec definisce quali sono i luoghi e come lavorarci. Direi che questa non è la migliore fonte da cui imparare e probabilmente è meglio spiegarla in qualche libro introduttivo di Lisp.

3

Uso defstruct:

> (defstruct state score children val something-else) 
STATE 
> (setq initial-state (make-state :score 0 :children nil :val 0 :something-else nil)) 
#S(STATE :SCORE 0 :CHILDREN NIL :VAL 0 :SOMETHING-ELSE NIL) 
> (state-score initial-state) ; current score 
0 
> (setf (state-score initial-state) 10) ; set new score 
10 
> (state-score initial-state) 
10 
+0

@Vijay: usare 'defstruct' è davvero meglio di una lista. Tuttavia, nelle liste dei corsi di base vengono spesso insegnate prima perché sono più semplici e più portabili tra i sapori di lisp (Scheme, ecc.) –

3

Questo accade perché setf è una macro. Quando si definisce state-score come una macro, setf vede:

(setf (nth 2 state) value) 

e sa cosa fare in quanto può utilizzare nth come un luogo per memorizzare i valori. D'altra parte, quando state-score è una funzione, setf vede solo un valore restituito e non può fare nulla al riguardo.

Maggiori informazioni su come funziona setf e il suo concetto di posti per una comprensione più approfondita.Here's un tutorial interessante che dice:

SETF forma speciale utilizza primo parametro definire un posto in memoria, valuta il suo secondo argomento, e memorizza il valore risultante di risultante locazione di memoria

+0

setf non è un modulo speciale. http://l1sp.org/cl/3.1.2.1.2.1 –

+0

@ Luís Oliveira: grazie, corretto su "macro" –

4

si può usare qualcosa di simile:

(defun get-score (state) 
    (nth 0 state)) ; This corresponds to the comments in the init function 

(defun set-score (state new-value) 
    (setf (nth 0 state) new-value)) 

(defsetf get-score set-score) 

In questo modo, ogni volta che si WR ite (setf (get-score something) else) sarà tradotto in (set-score something else).

Problemi correlati