#dictionary #clojure #macros #symbols
#словарь #clojure #макросы #символов
Вопрос:
Мне нужно использовать мой макрос как функции map, но я не знаю, как я могу это сделать. Объясните мне, пожалуйста. Мой макрос «get-conj» создает двоичные функции, которые состоят из объединения нескольких переменных.
Я уже пробовал поместить макрос в лямбда-функцию, но это не работает. Также я проверил макрос, и это правильно.
; this call of map doesn't work.
(map #(get-conj [x0 x1] %) [[1 0] [0 1] [0 0]])
;but just call of get-conj works fine.
(def conjunction (get-conj [x0 x1] [1 0]) ==> x0 amp; not(x1)
(conjunction true false) ; ==> true
(conjunction true true) ; ==> false
; bit is 1 or 0 value
(defn get-var [bit]
(if (zero? bit)
not
identity))
(defn get-args [n]
(vec (map #(read-string (s/join [x %])) (range n))))
(defn mapdef [fns args] (map #(%1 %2) fns args))
;; the macro builds binary functions which consist of
(defmacro get-conj [args bits] ;conjunction of few variables.
(let [vars (map get-var bits)]
`(fn ~args
(every? identity (mapdef '~vars ~args)))))
I want to get list of binary-functions.
Трассировка стека для:
(map #(get-conj [x0 x1] %) [[1 0] [0 1] [0 0]])
2. Unhandled clojure.lang.Compiler$CompilerException
Error compiling *cider-repl clojure/ex1:localhost:40769(clj)* at (1252:16)
Compiler.java: 7010 clojure.lang.Compiler/analyzeSeq
Compiler.java: 6773 clojure.lang.Compiler/analyze
Compiler.java: 6729 clojure.lang.Compiler/analyze
Compiler.java: 6100 clojure.lang.Compiler$BodyExpr$Parser/parse
Compiler.java: 5460 clojure.lang.Compiler$FnMethod/parse
Compiler.java: 4022 clojure.lang.Compiler$FnExpr/parse
Compiler.java: 7001 clojure.lang.Compiler/analyzeSeq
Compiler.java: 6773 clojure.lang.Compiler/analyze
Compiler.java: 6729 clojure.lang.Compiler/analyze
Compiler.java: 3881 clojure.lang.Compiler$InvokeExpr/parse
Compiler.java: 7005 clojure.lang.Compiler/analyzeSeq
Compiler.java: 6773 clojure.lang.Compiler/analyze
Compiler.java: 6729 clojure.lang.Compiler/analyze
Compiler.java: 6100 clojure.lang.Compiler$BodyExpr$Parser/parse
Compiler.java: 5460 clojure.lang.Compiler$FnMethod/parse
Compiler.java: 4022 clojure.lang.Compiler$FnExpr/parse
Compiler.java: 7001 clojure.lang.Compiler/analyzeSeq
Compiler.java: 6773 clojure.lang.Compiler/analyze
Compiler.java: 7059 clojure.lang.Compiler/eval
Compiler.java: 7025 clojure.lang.Compiler/eval
core.clj: 3206 clojure.core/eval
core.clj: 3202 clojure.core/eval
main.clj: 243 clojure.main/repl/read-eval-print/fn
main.clj: 243 clojure.main/repl/read-eval-print
main.clj: 261 clojure.main/repl/fn
main.clj: 261 clojure.main/repl
main.clj: 177 clojure.main/repl
RestFn.java: 137 clojure.lang.RestFn/applyTo
core.clj: 657 clojure.core/apply
core.clj: 652 clojure.core/apply
regrow.clj: 18 refactor-nrepl.ns.slam.hound.regrow/wrap-clojure-repl/fn
RestFn.java: 1523 clojure.lang.RestFn/invoke
interruptible_eval.clj: 79 nrepl.middleware.interruptible-eval/evaluate
interruptible_eval.clj: 55 nrepl.middleware.interruptible-eval/evaluate
interruptible_eval.clj: 142 nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn
AFn.java: 22 clojure.lang.AFn/run
session.clj: 171 nrepl.middleware.session/session-exec/main-loop/fn
session.clj: 170 nrepl.middleware.session/session-exec/main-loop
AFn.java: 22 clojure.lang.AFn/run
Thread.java: 748 java.lang.Thread/run
1. Caused by java.lang.IllegalArgumentException
Don't know how to create ISeq from: clojure.lang.Symbol
RT.java: 550 clojure.lang.RT/seqFrom
RT.java: 530 clojure.lang.RT/seq
core.clj: 137 clojure.core/seq
core.clj: 2738 clojure.core/map/fn
LazySeq.java: 40 clojure.lang.LazySeq/sval
LazySeq.java: 49 clojure.lang.LazySeq/seq
RT.java: 528 clojure.lang.RT/seq
core.clj: 137 clojure.core/seq
core_print.clj: 53 clojure.core/print-sequential
core_print.clj: 174 clojure.core/fn
core_print.clj: 174 clojure.core/fn
MultiFn.java: 233 clojure.lang.MultiFn/invoke
core.clj: 3666 clojure.core/pr-on
core.clj: 3669 clojure.core/pr
core.clj: 3669 clojure.core/pr
AFn.java: 154 clojure.lang.AFn/applyToHelper
RestFn.java: 132 clojure.lang.RestFn/applyTo
core.clj: 657 clojure.core/apply
core.clj: 4702 clojure.core/pr-str
core.clj: 4702 clojure.core/pr-str
RestFn.java: 408 clojure.lang.RestFn/invoke
debug.clj: 188 cider.nrepl.middleware.debug/pr-short
debug.clj: 182 cider.nrepl.middleware.debug/pr-short
debug.clj: 334 cider.nrepl.middleware.debug/read-debug-command
debug.clj: 312 cider.nrepl.middleware.debug/read-debug-command
debug.clj: 506 cider.nrepl.middleware.debug/break
debug.clj: 488 cider.nrepl.middleware.debug/break
REPL: 49 ex1.bfg/eval20195/get-conj
AFn.java: 165 clojure.lang.AFn/applyToHelper
AFn.java: 144 clojure.lang.AFn/applyTo
Var.java: 702 clojure.lang.Var/applyTo
Compiler.java: 6912 clojure.lang.Compiler/macroexpand1
Compiler.java: 6989 clojure.lang.Compiler/analyzeSeq
Compiler.java: 6773 clojure.lang.Compiler/analyze
Compiler.java: 6729 clojure.lang.Compiler/analyze
Compiler.java: 6100 clojure.lang.Compiler$BodyExpr$Parser/parse
Compiler.java: 5460 clojure.lang.Compiler$FnMethod/parse
Compiler.java: 4022 clojure.lang.Compiler$FnExpr/parse
Compiler.java: 7001 clojure.lang.Compiler/analyzeSeq
Compiler.java: 6773 clojure.lang.Compiler/analyze
Compiler.java: 6729 clojure.lang.Compiler/analyze
Compiler.java: 3881 clojure.lang.Compiler$InvokeExpr/parse
Compiler.java: 7005 clojure.lang.Compiler/analyzeSeq
Compiler.java: 6773 clojure.lang.Compiler/analyze
Compiler.java: 6729 clojure.lang.Compiler/analyze
Compiler.java: 6100 clojure.lang.Compiler$BodyExpr$Parser/parse
Compiler.java: 5460 clojure.lang.Compiler$FnMethod/parse
Compiler.java: 4022 clojure.lang.Compiler$FnExpr/parse
Compiler.java: 7001 clojure.lang.Compiler/analyzeSeq
Compiler.java: 6773 clojure.lang.Compiler/analyze
Compiler.java: 7059 clojure.lang.Compiler/eval
Compiler.java: 7025 clojure.lang.Compiler/eval
core.clj: 3206 clojure.core/eval
core.clj: 3202 clojure.core/eval
main.clj: 243 clojure.main/repl/read-eval-print/fn
main.clj: 243 clojure.main/repl/read-eval-print
main.clj: 261 clojure.main/repl/fn
main.clj: 261 clojure.main/repl
main.clj: 177 clojure.main/repl
RestFn.java: 137 clojure.lang.RestFn/applyTo
core.clj: 657 clojure.core/apply
core.clj: 652 clojure.core/apply
regrow.clj: 18 refactor-nrepl.ns.slam.hound.regrow/wrap-clojure-repl/fn
RestFn.java: 1523 clojure.lang.RestFn/invoke
interruptible_eval.clj: 79 nrepl.middleware.interruptible-eval/evaluate
interruptible_eval.clj: 55 nrepl.middleware.interruptible-eval/evaluate
interruptible_eval.clj: 142 nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn
AFn.java: 22 clojure.lang.AFn/run
session.clj: 171 nrepl.middleware.session/session-exec/main-loop/fn
session.clj: 170 nrepl.middleware.session/session-exec/main-loop
AFn.java: 22 clojure.lang.AFn/run
Thread.java: 748 java.lang.Thread/run
Комментарии:
1. Опубликуйте полную ошибку с помощью trace вместе с примером ввода, который вы используете.
2.
bits
Передается как символ? Вы уверены, что вам нужен макрос здесь?3. @Carcigenicate, я добавил трассировку стека. И да,
bits
является символом. Я хочу получить список сгенерированных двоичных функций, поэтому я пытаюсь написать макрос. Я думаю, что могу сделать это с помощью макросов. Я понимаю, что макросы вычисляются раньше, чем функции, но я не знаю, как я должен это использовать.4. Подумайте о том, что
bits
задано, чтоget-conj
является макросом. Это не те данные, которые вы ожидаете.5. @Carcigenicate, я думал об этом. Спасибо!
Ответ №1:
Если я правильно понимаю ваш код, у вас есть набор битов, скажем [0 1 0]
. Каждый бит указывает, следует ли отрицать соответствующий аргумент или нет. Затем вы хотите вычислить объединение этих, возможно, отрицаемых аргументов. Итак, если бы ваши аргументы были a
, b
и c
с теми битами, которые вы хотели бы вычислить (and (not a) b (not c))
, верно?
Как другие уже намекали в комментариях, вам не нужны макросы для достижения этого.
Вот функция get-conj
, которая в качестве аргументов принимает последовательность битов и возвращает новую функцию, которая выполняет то, что я объяснил выше:
(defn get-conj [bits]
(fn [amp; args]
{:pre [(= (count bits)
(count args))]}
(every? identity (map (fn [bit arg]
(if (zero? bit)
(not arg) arg))
bits args))))
Обратите внимание, что get-conj
функция теперь принимает только один аргумент, который является bits
.
И приведенный вами пример кода работает (с небольшими изменениями):
(map get-conj [[1 0] [0 1] [0 0]])
(def conjunction (get-conj [1 0]))
(assert (conjunction true false))
(assert (not (conjunction true true)))
Макросы лучше всего использовать в ситуациях, когда функций недостаточно, таких как небольшие синтаксические настройки (например, when
, ->
) или создание мини-языков, таких как core.async или англиканский вероятностный язык программирования.
Комментарии:
1. Хорошо, вы правы. Это означает, что я могу реализовать свой алгоритм без использования макросов. Большое спасибо! Я оценил вашу помощь!
Ответ №2:
Это связано с проблемами оценки аргументации, когда синтаксису #() передается символ макроса… Я считаю, что это «принципиально та же проблема, которая предотвращает вложенные макросы reader [т. Е. вы не можете #(x # (y))]. Возможно, я не лучший человек, который мог бы это объяснить.
Для вашего варианта использования одним из обходных путей является создание функции, которая собирает и оценивает вызовы макросов и передает часть этого в map … такого рода вещи всегда довольно сложны, но вот пример. Вам нужно будет заключить аргументы символов в кавычки.
(defn macro-partial [macro-name amp; supplied-args]
(apply partial (comp eval macroexpand list) macro-name supplied-args))
;produces a list of functions created by get-conj.
(map (macro-partial 'get-conj '[x0 x1]) [[1 0] [0 1] [0 0]])
;call like this
(map apply
(map (macro-partial 'get-conj '[x0 x1]) [[1 0] [0 1] [0 0]])
(repeat '(1 2)))