Clojureql - не может принимать значение макроса (clojure) - PullRequest
3 голосов
/ 07 февраля 2012

Мне нужно сгенерировать следующее:

(clojureql.core/join (clojureql.core/table db :tableA) (clojureql.core/table db :tableA)
     (clojureql.core/where (apply and (= :tableA.id :tableB.id)
                      [(apply or [(clojureql.predicates/in :tableA.id [1 2])
                                  (clojureql.predicates/in :tableA.id [3 4])])])))

Но «применить» или «форму» нужно «передать» из другой функции или макроса.Вот что я имею в виду:

(clojureql.core/join (clojureql.core/table db :tableA) (clojureql.core/table db :tableA)
     (clojureql.core/where (apply and (= :tableA.id :tableB.id)
                      [(my-or-f)])))

где эта функция:

(defn my-or-f []
    (apply or [(clojureql.predicates/in :tableA.id [1 2])
         (clojureql.predicates/in :tableA.id [3 4])]))

однако эта функция выдает это исключение:

#<CompilerException java.lang.Exception: Can't take value of a macro: #'clojure.core/or (NO_SOURCE_FILE:2)>

Я также пытался использоватьмакрос.Он компилируется, но выдает то же исключение, когда я пытаюсь выполнить запрос, используя этот макрос

(defmacro my-or-f []
    `(apply or [(clojureql.predicates/in :tableA.id [1 2])
         (clojureql.predicates/in :tableA.id [3 4])]))

Есть ли другой способ, которым я мог бы использовать "apply or"?

Спасибо.

Ответы [ 2 ]

4 голосов
/ 08 февраля 2012

Проблема заключается в том, что макросы не являются функциями - они выполняются во время компиляции и передаются с фактическим синтаксисом (прежде всего списками и символами), поэтому их нельзя применять во время выполнения к структурам данных времени выполнения. Таким образом, (apply or my-list) выдает ошибку. У вас есть несколько вариантов:

(def my-list (list false false 4 false 5))

(some identity my-list) -- returns 4
(reduce #(or %1 %2) my-list) -- returns 4

На практике вы, вероятно, захотите some - он останавливается, как только достигает элемента, который возвращает true, тогда как reduce всегда пересекает весь список.

Примечание: у вас также будет такая же проблема с вашим (apply and ...) - (every? identity ...) будет делать то, что вы хотите.

В этом случае, однако, зачем вам вообще нужно использовать apply? Это должно работать одинаково хорошо:

(clojureql.core/join (clojureql.core/table db :tableA) (clojureql.core/table db :tableA)
                     (clojureql.core/where (and (= :tableA.id :tableB.id)
                                                (my-or-f))))

(defn my-or-f []
  (or (clojureql.predicates/in :tableA.id [1 2])
      (clojureql.predicates/in :tableA.id [3 4])))

Последнее предложение - когда вы require clojureql.predicates заменяете clojureql.predicates на [clojurecl.predicates :as qlp], вы можете использовать qlp вместо clojureql.predicates в остальной части файла, что делает (если мы также заменим clojureql.core на qlc):

(qlc/join (qlc/table db :tableA) (qlc/table db :tableA)
          (qlc/where (and (= :tableA.id :tableB.id)
                          (my-or-f))))

(defn my-or-f []
  (or (qlp/in :tableA.id [1 2])
      (qlp/in :tableA.id [3 4])))

, который намного легче читать.

Для целей ClojureQL попробуйте (apply clojureql.predicates/or* ...) и (apply clojureql.predicates/and* ...). where от clojureql заменяет любые and и or, которые он видит, на clojureql.predicates/and* и clojureql.or*, поэтому выполнение этой замены самостоятельно должно работать. И and*, и or* являются функциями, а не макросами, поэтому проблем быть не должно.

1 голос
/ 08 февраля 2012

Если известно количество параметров, которые вы задаете or, это, вероятно, самое простое решение:

(apply #(or %1 %2) [nil 10]) ; => 10
...