2017-09-23 2 views
1

Ich möchte ein ACL-Modul schreiben, indem Sie ein Makro schreiben, dieses Makro ist das Ergebnis jedes Funktionsaufrufs im Makro zu überprüfen, wenn eine false zurückgibt, dann wird die ACL fehlschlagen, ohne zu laufen der folgende Funktionsaufruf. Wenn eine wahr zurückgibt und es noch Funktionsaufrufe zu überprüfen gibt, überprüfen Sie die folgenden bis zum letzten.clojure Makro unquote Spleißen Liste der Funktionsaufrufe

(defmacro checks 
    [head & tail] 
    `(let [status# ~head] 
    (if (and (true? status#) 
       (seq '~tail)) 
     (checks [email protected]) 
     status#))) 

Ich werde dieses Makro wie folgt aufrufen:

(checks (module1 args) (module2 args))` 

aber dies wird für (check [email protected]) in der Makrodefinition scheitern. Das Problem ist, dass ich Unquote Splicing die Liste aber ohne jede Funktion in der Liste aufrufen möchte.

+0

Nur ein Tipp, verwenden Sie keine Backticks zum Formatieren großer Codeabschnitte. Um es durch vier Leerzeichen zu markieren, drücken Sie Strg + k. – Carcigenicate

+0

Wird nicht 'und' Makro dasselbe tun? –

+0

Danke euch für die Kommentare. –

Antwort

1

Ich habe den Weg gefunden, um dieses Problem zu lösen:

(defmacro checks 
    [head & tail] 
    (let [sym (gensym)] 
    `(let [~sym ~head] 
     (if ~sym 
     ~(if tail 
      `(checks [email protected]) 
      sym) 
     ~sym)))) 

Verwendung Syntax unquote wieder auf Außen der (checks [email protected]) Form.

+0

Reiniger als [meine] (https://Stackoverflow.com/a/46378055/1562315). Es ist nur ['and'] (https://github.com/clojure/clojure/blob/clojure-1.9.0-alpha14/src/clj/clojure/core.clj#L834) mit einem anderen Namen, ohne die Nullarity . – Thumbnail

0

Das Problem besteht darin, dass Sie den Code für den rekursiven Aufruf des Makros generieren, obwohl kein Argument vorhanden ist.

(macroexpand-1 '(checks 1)) 
=> 
(clojure.core/let 
[status__1973__auto__ 1] 
(if 
    (clojure.core/and (clojure.core/true? status__1973__auto__) (clojure.core/seq (quote nil))) 
    (user/checks) 
    status__1973__auto__)) 

Der innere Makroaufruf schlägt fehl, weil mindestens ein Argument benötigt wird. Unabhängig davon, wie viele Argumente Sie dem Makro zur Verfügung stellen, wird durch die Erweiterung möglicherweise der fehlgeschlagene Aufruf generiert.

Sie müssen feststellen, ob es etwas gibt, in tail vor den rekursiven Aufruf zu checks Erzeugung:

(defmacro checks 
    [head & tail] 
    (if-not tail 
    head 
    `(let [status# ~head] 
     (if (true? status#) 
     (checks [email protected]) 
     status#)))) 

Jetzt

(macroexpand-1 '(checks 1)) 
=> 1 

(checks 1) 
=> 1 

Und

(macroexpand-1 '(checks 1 2 3)) 
=> 
(clojure.core/let 
[status__2010__auto__ 1] 
(if (clojure.core/true? status__2010__auto__) (user/checks 2 3) status__2010__auto__)) 

Außerdem

(checks 1 2 3) 
=> 1 
(checks true true 3) 
=> 3 

Wie @ PiotrekBzdyls Kommentar suggeriert, ist dies nur ein and Makro, das alles andere als true als falsch behandelt.

+0

Danke. Dieses Makro wird den falschen rekursiven Aufruf generieren. –