2010-12-26 6 views
11

Gegeben eine Liste von Werten, ich möchte die Liste auf T reduzieren, wenn alle Elemente nicht NIL sind, NIL wenn nicht. Das gibt mir eine Fehlermeldung:Wie reduziert man eine Liste boolescher Werte in Common Lisp?

(apply #'and (get-some-list)) 

Wie tut dies:

(reduce #'and (get-some-list)) 

Dies ist das beste, das ich gekommen bin oben mit:

[11]> (defun my-and (x y) (and x y)) 
MY-AND 

[12]> (reduce #'my-and '(T T T T T)) 
T 

[13]> (reduce #'my-and '(T T T T NIL)) 
NIL 

Warum ist "#" und" ungültig ? Gibt es eine mehr idiomatische Möglichkeit, dies in Common Lisp zu tun?

Antwort

8

#'and ist ungültig, weil and ein Makro ist, keine Funktion.

Sie können mit bekommen um eine benannte Funktion zu definieren, durch einen Lambda mit:

(reduce (lambda (x y) (and x y)) (get-some-list) :initial-value t) 

obwohl keine Abkürzung wie #' Es gibt. Alternativ können Sie auch every mit der identify-Funktion als Prädikat verwenden.

+1

Ihr Beispiel ist ** falsch **, weil das erste Argument von 'reduce'" mit null oder zwei Argumenten aufgerufen werden kann "(siehe CL hyperspec). Außerdem geben Sie keinen "Anfangswert" an. Als Ganzes funktioniert Ihr Beispiel nicht, wenn Sie die leere Liste oder Listen mit der Länge 1 reduzieren wollen. Eine korrekte Version ist zB '(Lambda (& optional (x T) (y T)) (und xy))' . –

3

Sie können das Symbol "sharp-quote" nur mit normalen Funktionen verwenden.

Note that only ordinary functions can be quoted with #’. It is an error to 
quote a macro function or special function this way, or to quote a symbol with 
#’ if that symbol does not name a function. 

> #’if 
Error: IF is not an ordinary function. 

COMMON LISP: A Gentle Introduction to Symbolic Computation, page 202

19

können Sie verwenden, um die jede Funktion:

(every #'identity '(T T T T T)) -> T 

und

(every #'identity '(T T T T NIL)) -> NIL 

Wahrscheinlich die effizienteste Art und Weise unter Verwendung von LOOP:

(loop for element in '(T T T T nil) always element) -> NIL 

Der Vorteil ist, dass keine Funktionsaufrufe über die Listenelemente benötigt werden.

#' ist ein Lese-Makro, das beim Lesen des Ausdrucks in FUNCTION expandiert. Also #'and ist (FUNKTION UND).

FUNCTION ist hier beschrieben: http://www.lispworks.com/documentation/HyperSpec/Body/s_fn.htm

Funktion nimmt einen Funktionsnamen oder einen Lambda-Ausdruck und gibt das entsprechende Funktionsobjekt.

und wird hier definiert: http://www.lispworks.com/documentation/HyperSpec/Body/m_and.htm

Er sagt, dass es sich um ein Makro, keine Funktion. Die Konsequenz ist, dass (FUNCTION AND) nicht funktioniert, da FUNCTION eine Funktion und kein Makro benötigt, um das entsprechende Funktionsobjekt zurückzugeben. Wie sepp2k in seiner Antwort beschreibt, können Sie eine Funktion mit LAMBDA erstellen und das Makro AND innerhalb dieser Funktion verwenden. Makros können nicht als Werte übergeben und später über FUNCALL oder APPLY aufgerufen werden. Dies funktioniert nur mit Funktionen.

Diese Lösung wird geschrieben als

(reduce (lambda (x y) (and x y)) (get-some-list)) 

LAMBDA ist ein Makro, das (lambda (...) ...) in (function (lambda (...) ...)) erweitert.

oben So ist wirklich:

(reduce (function (lambda (x y) (and x y))) (get-some-list)) 

, die geschrieben werden kann als

(reduce #'(lambda (x y) (and x y)) (get-some-list)) 

FUNCTION benötigt wird, da Common Lisp eine Differenz zwischen dem Namensraum für Werte und Funktionen macht. REDUCE muss die Funktion als Argument nach Wert übergeben. Also müssen wir die Funktion aus dem Funktionsnamensraum abrufen - was der Zweck von FUNCTION ist. Wann immer wir ein Funktionsobjekt übergeben wollen, müssen wir es aus dem Funktionsnamensraum holen.

Zum Beispiel im Fall einer lokalen Funktion:

(flet ((my-and (x y) (and x y))) 
    #'my-and) 

LAMBDA als Service-Makro, das erweitert wird (FUNCTION (LAMBDA ...)) wurde bei der Konstruktion von Common Lisp hinzugefügt.