2012-02-07 3 views
3

Мне нужно создать следующее:Clojureql - не может принимать значение макроса (Clojure)

(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])])) 

Есть еще один способ, который я мог бы использовать «применять или»?

Спасибо.

ответ

4

Вопрос заключается в том, что макросы не являются функциями - они работают во время компиляции и передаются фактический синтаксис (списки и символы, в первую очередь), поэтому вы не можете применять их во время выполнения к структурам времени исполнения. Таким образом, (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 - он останавливается, как только он достигает элемент, который возвращает истину, в то время как 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* ...). clojureql's where заменяет любые и or, которые он видит с clojureql.predicates/and* и clojureql.or*, поэтому выполнение этой замены самостоятельно должно работать. Оба and* и or* являются функциями, а не макросами, поэтому проблем не должно быть.

+0

Retief, я пробовал ваше предложение, однако это не работает с библиотекой clojureql. вот sql, который он генерирует: SELECT tableA. *, tableA. * FROM tableA JOIN tableA ON ((tableA.id = tableB.id) И tableA.id IN (1,2)), но sql, который мне нужно сгенерировать (из моего первого рабочего примера): SELECT tableA. *, tableA. * FROM tableA JOIN tableA ON ((tableA.id = tableB.id) AND (tableA.id IN (1,2) ИЛИ tableA.id IN (3, 4))) –

+0

Проблема заключается в том, что clojureQL/where является макросом, который перемещается по исходному дереву, которое вы его передаете, ища математику, 'и' и' или'. В нормальной среде мой код будет работать нормально, но это не работает для clojureQL. Вероятно, есть обходной путь, но, к сожалению, я фактически не использую clojureQL, поэтому я этого не знаю. Одна вещь, которая может работать, - принять мое окончательное предложение (без каких-либо операций с списками) и сделать макрос my-or-f. – Retief

+0

@RonP проверить мое редактирование - я нашел информацию, проверив источник clojureql [здесь] (https://github.com/LauJensen/clojureql) – Retief

1

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

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

Количество параметров варьируется, поэтому я не могу их жестко кодировать. –

Смежные вопросы