2013-03-18 11 views
5

Ich finde mich in einer Situation, wenn ich mehrere Prädikat in eins kombinieren müssen. Gibt es eine Standardmethode, etwas Ähnliches wie compliment?Boolesche Funktoren in Lisp

Angenommen, es gibt mehrere einfache Prädikate (z.B. is-fruit-p, is-red-p, grows-on-trees-p etc.) und eine Liste von Objekten, von denen eine Teilmenge herausgefiltert werden müssen mehr als ein Prädikat verwendet wird. Was ist der bessere Weg, dies zu erreichen als die folgenden:

(remove-if #'is-fruit-p 
      (remove-if #'is-red-p 
         (remove-if #'grows-on-trees-p list-of-objects))) 
+4

'(defun Kompliment (Person & optional (Strom t)) (Format Stream "Hallo, ~ a, die Sie wirklich herrlicher Blick heute!" Person))' – Svante

Antwort

3

Ich bin mir nicht sicher, ob eine solche Funktion von der Box verfügbar ist. Wenn Sie Funktionen kombinieren müssen, die Sie in einer Kompilierzeit bestimmen können, können Sie ein Makro dazu schreiben. Wenn Sie Prädikatenfunktionen dynamisch erkennen müssen, können Sie eine Funktion schreiben, um die Liste der Funktionen zu loopen und die Ergebnisse bis zur falschen Bedingung zu akkumulieren.

kann das Makro wie folgt aussehen:

(defmacro combine-predicates (combine-func &rest preds) 
    (let ((x (gensym))) 
    `(lambda (,x) (,combine-func ,@(loop for p in preds 
         collecting `(funcall ,p ,x)))))) 

Und Sie können es wie diese Sie sicher

(remove-if (combine-predicates and 
           #'is-fruit-p 
           #'is-red-p 
           #'grows-on-trees-p) obj-list) 
+1

Danke, was Sie zum Entfernen von 'funcall' denken und '# '' s? – zzandy

+0

Ich bin mir nicht sicher, wie funcall die Leistung beeinflusst. Wenn Sie möchten, können Sie funcall entfernen. Eigentlich hatte ich Angst, dass das Passieren (Lambda (x) (...)) nicht funktionieren würde, aber zumindest in sbcl. Passing # '(Lambda (x) (...)) nicht. Wenn Sie nicht die alte Lambda-Syntax verwenden, können Funcalls entfernt werden. Es sei denn, Sie möchten eine Kompilierzeitvariable verwenden, um Prädikatfunktionen zur Kompilierzeit einzurichten. – JustAnotherCurious

+0

Beachten Sie, dass diese Funktion, wenn Sie Metatilities verwenden, bereits als 'conjoin' existiert. – Hugh

2
(let ((predicates '(zerop evenp))) 
    (remove-if (lambda (item) 
       (some (lambda (fn) (funcall fn item)) 
        predicates)) 
      '(0 1 2 3 4 0 1 2 3 4))) 
5

Sind verwenden, die eine spezielle Syntax wirklich helfen würde? Betrachten Sie die folgende

(lambda (x) 
    (and (is-fruit-p x) 
     (or (grows-on-tree-p x) 
      (is-red-p x)))) 

und jetzt die etwas allgemeinere

(lambda (x) 
    (and (is-fruit-p x) 
     (or (grows-on-tree-p x) 
      (eq (color x) 'red)))) 

oder

(lambda (x) 
    (and (is-fruit-p x) 
     (or (grows-on-tree-p x) 
      (eq (color x) desired-color)))) ; desired-color captured lexical 

Auch wenn Sie eine spezielle Syntax aufbauen für Prädikate denken Sie, die weitere Sprachen Komplexität ist es wert die Starrheit, die du bekommen wirst? Zum Beispiel werden Sie ein Prädikat #'weights-exactly-five-ounces-p definieren? Was ist mit #'weights-up-to-and-including-six-and-half-ounces-p?

Wenn Sie ein parametrisches Prädikat benötigen und dafür Lambda-Formulare verwenden, dann schreiben Sie mit einem Combiner mehr Code als Sie nicht verwenden, da der Wrapper (lambda (x) ...) für jeden parametrischen Term benötigt wird. Noch wichtiger ist, dass dieser Code auch schwieriger ist lesen (zusätzlich zu einem speziellen neuen Makro für Prädikat Kombination zu lernen).

IMO Es kann sinnvoll sein, zu schreiben und/oder Kombinatoren, wenn Sie in Prädikaten übergeben werden und Sie müssen Prädikate an jemand anderen übergeben ... aber nicht für den Code, den Sie in dem Beispiel verwendet haben; dafür würde ich schreiben

Weniger zu schreiben, weniger zu lesen, nichts zu lernen, trivial zu parametrisieren.

Angenommen, Sie möchten zum Beispiel eine Liste von Früchten mit der gleichen Farbe wie die, die Sie haben (in mine) und mit gleichem Gewicht oder möglicherweise schwerer ...

(remove-if-not (lambda (x) (and (is-fruit-p x) 
           (eq (color x) (color mine)) 
           (>= (weight x) (weight mine)))) 
       objects) 
+0

Ich bezweifle etwas wie '(remove-if (entweder is-rot-p is-fruit-p) Liste) ist schwer zu lesen. Es macht mir auch nichts aus, einen ähnlichen Code zu verwenden, nur dass ich eine große "Schleife" habe, wo ich 'remove-id' mit' is-ap', mit 'is-bp' und mit beiden verwenden muss 'is-ap' _und_' ist-bp', und das gab mir eine Idee, dass ich eine Verkürzung verwenden könnte. – zzandy

+0

Könntest du bitte ein wenig darüber erzählen, wie du dein letztes Beispiel parametrisieren kannst? – zzandy

3

Ein Ansatz erstklassige Funktionen:

(defun complement (func) 
    (lambda (x) (not (funcall func x)))) 

(defun conjoin (pred1 pred2) 
    (lambda (x) (and (funcall pred1 x) (funcall pred2 x)))) 

(defun disjoin (pred1 pred2) 
    (lambda (x) (or (funcall pred1 x) (funcall pred2 x)))) 

, von dem Sie

(remove-if (conjoin #'is-fruit-p (conjoin #'is-red-p #'grows-on-trees-p)) list-of-objects) 
4

Funktionen höherer Ordnung wie disjoin und conjoin sind in der quicklisp installierbar alexandria Bibliothek produzieren kann.

CL-USER> (ql:quickload "alexandria") 
... 
CL-USER> (remove-if (alexandria:disjoin #'zerop #'oddp #'minusp) 
        '(0 -1 1 -2 2)) 
=> (2) 
+0

Danke, das ist wahrscheinlich die beste Antwort. – zzandy