2010-09-08 4 views
14

Non ho molta familiarità con le macro Clojure/Lisp. Vorrei scrivere la macro apply-recur che avrebbe lo stesso significato di (apply recur ...)Applicare-ripetere la macro in Clojure

Immagino che non ci sia davvero bisogno di tale macro, ma penso che sia un buon esercizio. Quindi sto chiedendo la tua soluzione.

risposta

15

Bene, non ce n'è davvero bisogno, se solo perché recur non può assumere varargs (un recur in cima alla funzione richiede un unico argomento finale definibile che raggruppa tutti gli argomenti passano l'ultimo argomento richiesto). Ciò non influisce sulla validità dell'esercizio, naturalmente.

Tuttavia, v'è un problema in quanto una "corretta" apply-recur dovrebbe presumibilmente gestire seguenti del regolamento provvisorio argomenti restituiti da espressioni arbitrarie e non solo letterali:

;; this should work... 
(apply-recur [1 2 3]) 

;; ...and this should have the same effect... 
(apply-recur (vector 1 2 3)) 

;; ...as should this, if (foo) returns [1 2 3] 
(apply-recur (foo)) 

Tuttavia, il valore di un'espressione arbitraria come (foo) è semplicemente non disponibile, in generale, al momento dell'espansione della macro. (Probabilmente si assume che (vector 1 2 3) abbia sempre lo stesso valore, ma foo potrebbe significare cose diverse in momenti diversi (un motivo eval non funzionerebbe), essere un let locale piuttosto che un Var (un altro motivo eval non funzionerebbe) etc.)

Così a dare una completamente generale apply-recur, avremmo bisogno di essere in grado di determinare il numero di argomenti un regolare recur modulo si aspetta e hanno (apply-recur some-expression) espandere a qualcosa di simile

(let [seval# some-expression] 
    (recur (nth seval# 0) 
     (nth seval# 1) 
     ... 
     (nth seval# n-1))) ; n-1 being the number of the final parameter 

(La finale nth potrebbe essere necessario nthnext se abbiamo a che fare con varargs, che presenta un problema simile a quanto descritto nel prossimo paragrafo. Inoltre, sarebbe una buona idea aggiungere un'affermazione di verificare la lunghezza del seqable restituito da some-expression.)

Non sono a conoscenza di alcun metodo per determinare la corretta arity di un recur in un punto particolare nel codice al momento della macro-espansione. Ciò non significa che non sia disponibile - è qualcosa che il compilatore deve sapere comunque, quindi forse c'è un modo per estrarre quell'informazione dai suoi interni. Anche così, qualsiasi metodo per farlo dovrebbe quasi certamente fare affidamento su dettagli di implementazione che potrebbero cambiare in futuro.

Quindi la conclusione è questa: anche se è possibile scrivere una tale macro (che potrebbe anche non essere il caso), è probabile che qualsiasi implementazione sarebbe molto fragile.


Come osservazione finale, crei un apply-recur che sarebbe solo essere in grado di trattare letterali (in realtà la struttura generale della seq arg dovrebbe essere dato come letterale argomenti stessi - non necessariamente, quindi questo potrebbe funzionare: (apply-recur [foo bar baz]) =>(recur foo bar baz)) sarebbe abbastanza semplice. Non sto rovinando l'esercizio dando via la soluzione, ma, come suggerimento, considera l'utilizzo di [email protected].

3

apply è una funzione che prende un'altra funzione come argomento. recur è un modulo speciale, non una funzione, quindi non può essere passato a apply.

+1

Certo, anche se ho preso la domanda per dire "dato che non si può semplicemente scrivere" (si applica ...), come si farebbe per scrivere una macro che catturerebbe il senso inteso di ciò. " –

+0

Sì Michał, intendevo così. Grazie a entrambi. – JoeCamel