Domanda

Sto leggendo i paradigmi della programmazione dell'intelligenza artificiale di Peter Norvig e ho riscontrato un problema che non posso risolvere da solo (questa è la mia introduzione a LISP). Il problema è piuttosto piccolo, davvero, ma ovviamente non uno che il mio piccolo cervello può risolvere.

Perché quando il valore di una funzione è un lambda, è un errore utilizzare quella funzione come primo elemento in un elenco. Per esempio:

Lisp:

(defun some-func ()
  #'(lambda (x) x))

;; At REPL
;; Does not work
> ((some-func) 1)
;; Does work
> ((lambda (x) x) 1)
;; Also works
> (funcall (some-func) 1)

Spero che abbia un senso!

È stato utile?

Soluzione

Questa è una buona domanda, e quella in cui Lisp comune può essere piuttosto confuso. Il fatto è che per motivi storici, LISP comune ha due spazi dei nomi: uno per le funzioni e un altro per i valori. Per far sì che ciò accada, ci sono due diverse regole di valutazione per la posizione di testa di un'applicazione di funzione e per il resto: il primo valuterà un simbolo come nome di funzione e il secondo valuterà un simbolo come riferimento variabile. Ci sono ovviamente casi in cui il valore è effettivamente una funzione, ad esempio se si scrive a mapcar funzione, ti consigliamo di fare qualcosa di simile

(defun my-mapcar (f l)
  (if (null l)
    '()
    (cons (f (car l)) (my-mapcar f (cdr l)))))

Ma questo non funzionerà - si lamentarà f essere una funzione sconosciuta. Per questi casi c'è una funzione speciale chiamata funcall, che riceve una funzione e argomento per la funzione e applicherà la funzione come al solito - e da allora funcall è una funzione semplice, i suoi argomenti sono tutti valutati come al solito (come valori). Quindi quanto sopra dovrebbe essere risolto usandolo:

(defun my-mapcar (f l)
  (if (null l)
    '()
    (cons (funcall f (car l)) (my-mapcar f (cdr l)))))

Come probabilmente sospetti ora, ci sono i casi di specchio: in cui vuoi valutare qualcosa come una funzione e non come valore. Ad esempio, questo non funziona:

(my-mapcar 1+ '(1 2 3))

Perché si riferisce al 1+ variabile e non la funzione. Per questi casi c'è una forma speciale chiamata function Ciò valuta il suo contenuto come una funzione e lo restituisce come valore:

(my-mapcar (function 1+) '(1 2 3))

e può essere abbreviato con #':

(my-mapcar #'1+ '(1 2 3))

Non è la fine di questa storia: dare alcuni esempi:

  • In alcuni casi un semplice nome citato può funzionare come funzione, ad esempio '1+ Nell'ultimo esempio funziona - ma questo è una specie di hack che può vedere solo nomi legati a livello globale, quindi #' è quasi sempre meglio

  • Un hack simile può essere usato con lambda - Quindi puoi usare (my-mapcar '(lambda (x) (1+ x)) '(1 2 3)), in effetti, potresti usare (list 'lambda '(x) '(1+ x)) Il che è ancora peggio (e IIRC, non portabile), ma usando (lambda (x) (1+ x)) funziona poiché è implicitamente avvolto in a #' (Prova ad espandere a lambda forma come una macro e la vedrai). Un hack correlato fa bene usare un lambda Espressione come capo di un'applicazione di funzione (che è una delle cose che hai provato).

  • let ecc. Lega i valori locali e in alcuni casi vorrai invece legare le funzioni locali - per questo ci sono nuovi costrutti vincolanti: flet e labels

Se tutto ciò sembra strano e/o eccessivamente complicato, allora non sei solo. È una delle principali differenze tra LISP e schema comuni. (La differenza porta quindi a cambiamenti nei modi comuni in entrambe le lingue: il codice dello schema tende a utilizzare le funzioni di ordine superiore molto più frequentemente del codice LISP comune. Come al solito con questo tipo di domande religiose, alcune persone sostengono a favore di ciò che fa CL, sostenendo Che le funzioni di ordine superiore sono confuse, quindi a loro piace il promemoria esplicito nel codice.)

Altri suggerimenti

((lambda (x) ...) ...) è un caso speciale con codice rigido nelle regole del valutatore. Non sta valutando il primo elemento nella forma e usando il risultato come caso generale. È normale che devi usare funcall o apply Per chiamare una funzione che è il risultato della valutazione di qualche altra forma.

Il motivo per cui LISP comune non consente a una funzione che restituisce un lambda per essere il primo elemento in un'espressione ha a che fare con la distinzione tra LISP-1 e LISP-2.

Se consentito LISP comune ((some-func) 1) Essere equivalenti a (funcall (some-func) 1), allora potrebbe essere percepito come incoerente per non permettere anche (let ((f #'some-func)) (f 1)) piuttosto che richiedere (funcall f 1).

In realtà esiste un'ottima giustificazione per non supportare tali forme: sono ambigue. Considera il modo Nomi delle funzioni sono definiti in comune LISP:

Nome funzione n. 1. (in un ambiente) UN simbolo o a elenco (setf symbol) questo è il nome di un funzione In ciò ambiente. 2. a simbolo o a elenco (setf symbol).

Detto questo, come dovrebbe comportarsi una forma come il seguente?

((setf car) 10 x)

Dovrebbe questo set xauto al valore 10, o dovrebbe eseguire il modulo (setf car) (che sarebbe un errore) e provare a utilizzare il suo valore di ritorno come funzione da chiamare con gli argomenti 10 e x? LISP comune non specifica né comportamenti, e non mi è affatto chiaro che sia una cattiva scelta. Dopotutto, per quanto posso vedere, nulla nello standard impedisce alle implementazioni conformi di estendere la definizione di moduli validi per supportare una gamma più ampia di nomi degli operatori (quindi anche le funzioni SETF a causa di speciali non aiuterebbero davvero qui).

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