I have this function called not-elimination which takes an argument and applies the not inference rule which states: (not (not x)) infer x. So for example if my argument is '(not (not a)) then #{a} would be my output. Example 2, Argument: '(not (not (not a))) Output: #{(not a)}
The problem I am running into is the case where my argument is '(not x) which is supposed to return #{} (empty set), but I am getting the error below. Any ideas on what the problem is?
Execution error (IllegalArgumentException) at microproject2.core/not-elimination (core.clj:7).
Don't know how to create ISeq from: clojure.lang.Symbol
My code:
(ns microproject2.core)
(defn not-elimination [expression]
(if(not-empty expression)
(if (= (nth expression 1) "x" )
(set {})
(sorted-set-by > (last (last expression))))
(println "The list is empty")))
Here is an example that works, including some unit tests:
(ns tst.demo.core
(:use tupelo.core tupelo.test))
(defn not-elimination
[expr]
(if (not-empty? expr)
(if (= (symbol "x") (second expr))
#{} ; and empty set, could use `(hash-set)`
(sorted-set-by > (last (last expr))))
:the-list-is-empty))
(dotest
(is= #{} (hash-set)) ; both are equivalent
(is= :the-list-is-empty (not-elimination '()))
(is= (hash-set 'x) (not-elimination '(not (not x))))
(is= #{ '(not x) } (not-elimination '(not (not (not x)))))
)
It is based on this template project.
Related
This macro returns the values of the "magic" &env as a map, so that
(let [xyz "ZYX"] (get-env)) returns {xyz "ZYX"}, where the key is a Symbol.
(defmacro get-env []
(let [ks (keys &env)]
`(zipmap '~ks [~#ks])))
The expression '~ks evaluates the ks into Symbols at the macro-expansion phase (right?), but then quotes the expansion, so that the Symbols don't get evaluated into their values ("ZYX" in our example), but rather stay as Symbols (xyz). Is that right?
About [~#ks]: It evaluates ks into an seq of Symbols at the macro-expansion phase (right?) (and splices them and forms a vector with []). But how does that allow these Symbols to get further evaluated into their values ("ZYX" in our example) -- is there a second evaluation step, applied immediately after the first?
Another variant is
(defmacro local-env [] (->> (keys &env)
(map (fn [k] [(list 'quote k) k])) (into {})))
Your macro takes all the keys from the env. Then it uses the keys (a
list of symbols) to zip both the list of keys with spliced symbols
inside a vector. So what you get from
(let [x 42]
(get-env))
is
(let [x 42]
(zipmap '(x) [x]))
This is a compile-time transformation of your code (the whole point of
macros). The resulting code at runtime will use the 42 from the bound
x.
Preface
You may also be interested in the book "Clojure Macros", and in this StackOverflow question:
How do I write a Clojure threading macro?
Discussion
When in doubt, ask the compiler. Consider this code using my favorite template project:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(defmacro getenv []
(prn :env &env)
(prn :env-meta (meta &env))
(prn :form &form)
(prn :form-meta (meta &form)))
(defn go []
(newline)
(prn :01)
(getenv)
(let [x 1
y "yyy"]
(newline)
(prn :02)
(getenv))
)
(dotest
(go))
with output
:env nil
:env-meta nil
:form (getenv)
:form-meta {:line 15, :column 3}
:env {x #object[clojure.lang.Compiler$LocalBinding 0x1ab07559 "clojure.lang.Compiler$LocalBinding#1ab07559"], y #object[clojure.lang.Compiler$LocalBinding 0x26c79134 "clojure.lang.Compiler$LocalBinding#26c79134"]}
:env-meta nil
:form (getenv)
:form-meta {:line 21, :column 5}
Testing tst.demo.core
:01
:02
so we can see the 4 (prn ...) outputs for each call to getenv. In the case where there are no local bindings, we get
&env ;=> nil
and for the case with the let we get a map like
(let [env-val (quote
{x :compiler-local-1
y :compiler-local-1})
ks (keys env-val)
ks-vec [ks]
]
(spyx env-val)
(spyx ks)
(spyx ks-vec)
)
with result
env-val => {x :compiler-local-1, y :compiler-local-1}
ks => (x y)
ks-vec => [(x y)]
At this point, I'm not quite sure what your desired result is. Could you modify the question to add that?
BTW, there is no hidden 2nd step, if I understand your question correctly.
Also
I rewrite your local-env and got the following result:
(defmacro local-env []
(prn :into-result
(into {}
(mapv
(fn [k] [(list 'quote k) k])
(keys &env)))))
(let [x 1
y "yyy"]
(newline)
(prn :03)
(local-env))
with result
:into-result {(quote x) x,
(quote y) y}
so I think there is some confusion here.
I am reading the book "Getting Clojure" by Russ Olsen. In chapter 8, "Def, Symbols, and Vars", there is the following function definition:
(def second (fn second [x] (first (next x))))
^^^^^^
My question is regarding the underlined second, which comes second.
At first, I thought this syntax is wrong as anonymous functions don't need a name. But as it turnes out, this syntax is correct.
Usage: (fn name? [params*] exprs*)
(fn name? ([params*] exprs*) +)
I tried comparing the following two function calls.
user> (fn second [x] (first (rest x)))
#function[user/eval5642/second--5643]
user> (fn [x] (first (rest x)))
#function[user/eval5646/fn-5647]
Besides the name of the function, there does not seem to be a difference.
Why would there be a name? argument to fn?
You can use it when creating multiple arities:
(fn second
([x] (second x 1))
([x y] (+ x y)))
or if you need to make a recursive call:
(fn second [x] (when (pos? x)
(println x)
(second (dec x))))
There are two main usages:
recursive functions (you now know the name)
user=> ((fn foo [x] (when (pos? x) (println x) (foo (dec x)))) 3)
3
2
1
nil
better stacktraces (the name will give you a better hint, where things went wrong)
user=> (map (fn bar [x] (inc x)) ["a"])
Error printing return value (ClassCastException) at clojure.lang.Numbers/inc (Numbers.java:137).
java.lang.String cannot be cast to java.lang.Number
user=> (pst)
ClassCastException java.lang.String cannot be cast to java.lang.Number
clojure.lang.Numbers.inc (Numbers.java:137)
user/eval8020/bar--8021 (NO_SOURCE_FILE:1)
clojure.core/map/fn--5866 (core.clj:2753)
clojure.lang.LazySeq.sval (LazySeq.java:42)
clojure.lang.LazySeq.seq (LazySeq.java:51)
clojure.lang.RT.seq (RT.java:535)
clojure.core/seq--5402 (core.clj:137)
clojure.core/seq--5402 (core.clj:137)
puget.printer.PrettyPrinter (printer.clj:529)
puget.printer/iseq-handler--1663 (printer.clj:314)
puget.printer/iseq-handler--1663 (printer.clj:312)
puget.printer/format-doc* (printer.clj:223)
(note user/eval8020/bar--8021)
I'd like to create a list depending on the results of some functions. In Java (my background), I'd do something like:
List<String> messages = ...
if(condition 1)
messages.add(message 1);
if(condition 2)
messages.add(message 2);
...
if(condition N)
messages.add(message N);
In clojure, I think I'll need to create a list using let like the following (just dummy example):
(let [result
(vec
(if (= 1 1) "message1" "message2")
(if (= 1 0) "message3" "message4"))]
result)
I've also checked cond but I need to be appending the elements to the list considering all the validations (and cond breaks after one condition is satisfied).
Which way should I follow to achieve this?
If you want them to be conditionally added like in the Java example, you could use cond->, which does not short circuit:
(let [messages []]
(cond-> messages ; Conditionally thread through conj
(= 1 1) (conj "Message1")
(= 0 1) (conj "Message2")
(= 0 0) (conj "Message3")))
=> ["Message1" "Message3"]
If you want to conditionally add one or the other like your second example suggests however, you could just use plain conj with some if expressions:
(let [messages []]
(conj messages
(if (= 1 1) "Message1" "Message2")
(if (= 0 1) "Message3" "Message4")))
=> ["Message1" "Message4"]
And I'll note that your original attempt almost worked. Instead of vec, you could have used vector, or just a vector literal:
(let [messages [(if (= 1 1) "Message1" "Message2")
(if (= 1 0) "Message3" "Message4")]]
messages)
=> ["Message1" "Message4"]
Although, this is would only be beneficial if you didn't already have a messages populated that you wanted to add to. If that was the case, you'd have to use concat or into:
(let [old-messages ["old stuff"]
messages [(if (= 1 1) "Message1" "Message2")
(if (= 1 0) "Message3" "Message4")]]
(into old-messages messages))
=> ["old stuff" "Message1" "Message4"]
Take a look at cond->.
For example, your Java example could be written like:
(cond-> (some-fn-returning-messages)
(= 1 1) (conj "message1")
(= 1 2) (conj "message2")
...
(= 1 n) (conj "messagen"))
I see several answers pointing to the cond-> macro which appears to match your request most closely in that it is nearest to the style outlined in your question.
Depending on the number of conditions you have, your question seems like a good candiate for simply using filter.
(def nums (range 10))
(filter #(or (even? %) (= 7 %)) nums)
If you have a bunch of conditions (functions), and "or-ing" them together would be unwieldy, you can use some-fn.
Numbers from 0-19 that are either even, divisible by 7, greater than 17, or exactly equal to 1. Stupid example I know, just wanted to show a simple use-case.
(filter (some-fn
even?
#(zero? (mod % 7))
#(> % 17)
#(= 1 %))
(range 20))
Looks like everyone had the same idea! I did mine with keywords:
(ns tst.demo.core
(:use tupelo.core demo.core tupelo.test))
(defn accum
[conds]
(cond-> [] ; append to the vector in order 1,2,3
(contains? conds :cond-1) (conj :msg-1)
(contains? conds :cond-2) (conj :msg-2)
(contains? conds :cond-3) (conj :msg-3)))
(dotest
(is= [:msg-1] (accum #{:cond-1}))
(is= [:msg-1 :msg-3] (accum #{:cond-1 :cond-3}))
(is= [:msg-1 :msg-2] (accum #{:cond-2 :cond-1}))
(is= [:msg-2 :msg-3] (accum #{:cond-2 :cond-3}))
(is= [:msg-1 :msg-2 :msg-3] (accum #{:cond-3 :cond-2 :cond-1 })) ; note sets are unsorted
)
If you want more power, you can use cond-it-> from the Tupelo library. It threads the target value through both the condition and the action forms, and uses the special symbol it to show where the threaded value is to be placed. This modified example shows a 4th condition where, "msg-3 is jealous of msg-1" and always boots it out of the result:
(ns tst.demo.core
(:use tupelo.core demo.core tupelo.test))
(defn accum
[conds]
(cond-it-> #{} ; accumulate result in a set
(contains? conds :cond-1) (conj it :msg-1)
(contains? conds :cond-2) (conj it :msg-2)
(contains? conds :cond-3) (conj it :msg-3)
(contains? it :msg-3) (disj it :msg-1) ; :msg-3 doesn't like :msg-1
))
; remember that sets are unsorted
(dotest
(is= #{:msg-1} (accum #{:cond-1}))
(is= #{:msg-3} (accum #{:cond-1 :cond-3}))
(is= #{:msg-1 :msg-2} (accum #{:cond-2 :cond-1}))
(is= #{:msg-2 :msg-3} (accum #{:cond-2 :cond-3}))
(is= #{:msg-2 :msg-3} (accum #{:cond-3 :cond-2 :cond-1 }))
)
Not necessarily relevant to your use case, and certainly not a mainstream solution, but once in a while I like cl-format's conditional expressions:
(require '[clojure.pprint :refer [cl-format]])
(require '[clojure.data.generators :as g])
(cl-format nil
"~:[He~;She~] ~:[did~;did not~] ~:[thought about it~;care~]"
(g/boolean) (g/boolean) (g/boolean))
A typical case would be validating a piece of data to produce an error list.
I would construct a table that maps condition to message:
(def error->message-table
{condition1 message1
condition2 message2
...})
Note that the conditions are functions. Since we can never properly recognise functions by value, you could make this table a sequence of pairs.
However you implement the table, all we have to do is collect the messages for the predicates that apply:
(defn messages [stuff]
(->> error->message-table
(filter (fn [pair] ((first pair) stuff)))
(map second)))
Without a coherent example, it's difficult to be more explicit.
First-class functions and the packaged control structures within filter and map give us the means to express the algorithm briefly and clearly, isolating the content into a data structure.
I have the following code:
(ns macroo)
(def primitives #{::byte ::short ::int})
(defn primitive? [type]
(contains? primitives type))
(def pp clojure.pprint/pprint)
(defn foo [buffer data schema]
(println schema))
(defmacro write-fn [buffer schema schemas]
(let [data (gensym)]
`(fn [~data]
~(cond
(primitive? schema) `(foo ~buffer ~data ~schema)
(vector? schema) (if (= ::some (first schema))
`(do (foo ~buffer (count ~data) ::short)
(map #((write-fn ~buffer ~(second schema) ~schemas) %)
~data))
`(do ~#(for [[i s] (map-indexed vector schema)]
((write-fn buffer s schemas) `(get ~data ~i)))))
:else [schema `(primitive? ~schema) (primitive? schema)])))) ; for debugging
(pp (clojure.walk/macroexpand-all '(write-fn 0 [::int ::int] 0)))
The problem is, upon evaluating the last expression, I get
=>
(fn*
([G__6506]
(do
[:macroo/int :macroo/int true false]
[:macroo/int :macroo/int true false])))
I'll explain the code if necessary, but for now i'll just state the problem (it might be just a newbie error I'm making):
`(primitive? ~schema)
and
(primitive? schema)
in the :else branch return true and false respectively, and since i'm using the second version in the cond expression, it fails where it shouldn't (I'd prefer the second version as it would be evaluated at compile time if i'm not mistaken).
I suspect it might have something to do with symbols being namespace qualified?
After some investigations (see edits), here is a working Clojure alternative. Basically, you rarely need recursive macros. If you
need to build forms recursively, delegate to auxiliary functions and call them from the macro (also, write-fn is not a good name).
(defmacro write-fn [buffer schemas fun]
;; we will evaluate "buffer" and "fun" only once
;; and we need gensym for intermediate variables.
(let [fsym (gensym)
bsym (gensym)]
;; define two mutually recursive function
;; to parse and build a map consisting of two keys
;;
;; - args is the argument list of the generated function
;; - body is a list of generated forms
;;
(letfn [(transformer [schema]
(cond
(primitive? schema)
(let [g (gensym)]
{:args g
:body `(~fsym ~schema ~bsym ~g)})
(sequential? schema)
(if (and(= (count schema) 2)
(= (first schema) ::some)
(primitive? (second schema)))
(let [g (gensym)]
{:args ['& g]
:body
`(doseq [i# ~g]
(~fsym ~(second schema) ~bsym i#))})
(reduce reducer {:args [] :body []} schema))
:else (throw (Exception. "Bad input"))))
(reducer [{:keys [args body]} schema]
(let [{arg :args code :body} (transformer schema)]
{:args (conj args arg)
:body (conj body code)}))]
(let [{:keys [args body]} (transformer schemas)]
`(let [~fsym ~fun
~bsym ~buffer]
(fn [~args] ~#body))))))
The macro takes a buffer (whatever it is), a schema as defined by your language and a function to be called for each value being visited by the generated function.
Example
(pp (macroexpand
'(write-fn 0
[::int [::some ::short] [::int ::short ::int]]
(fn [& more] (apply println more)))))
... produces the following:
(let*
[G__1178 (fn [& more] (apply println more)) G__1179 0]
(clojure.core/fn
[[G__1180 [& G__1181] [G__1182 G__1183 G__1184]]]
(G__1178 :macroo/int G__1179 G__1180)
(clojure.core/doseq
[i__1110__auto__ G__1181]
(G__1178 :macroo/short G__1179 i__1110__auto__))
[(G__1178 :macroo/int G__1179 G__1182)
(G__1178 :macroo/short G__1179 G__1183)
(G__1178 :macroo/int G__1179 G__1184)]))
First, evaluate buffer and fun and bind them to local variables
Return a closure which accept one argument and destructures it according to the given schema, thanks to Clojure's destructuring capabilities.
For each value, call fun with the appropriate arguments.
When the schema is [::some x], accept zero or more values as a vector and call the function fun for each of those values. This needs to be done with a loop, since the size is only know when calling the function.
If we pass the vector [32 [1 3 4 5 6 7] [2 55 1]] to the function generated by the above macroexpansion, the following is printed:
:macroo/int 0 32
:macroo/short 0 1
:macroo/short 0 3
:macroo/short 0 4
:macroo/short 0 5
:macroo/short 0 6
:macroo/short 0 7
:macroo/int 0 2
:macroo/short 0 55
:macroo/int 0 1
In this line:
`(do ~#(for [[i s] (map-indexed vector schema)]
((write-fn buffer s schemas) `(get ~data ~i)))))
you are calling write-fn, the macro, in your current scope, where s is just a symbol, not one of the entries in schema. Instead, you want to emit code that will run in the caller's scope:
`(do ~#(for [[i s] (map-indexed vector schema)]
`((write-fn ~buffer ~s ~schemas) (get ~data ~i)))))
And make a similar change to the other branch of the if, as well.
As an aside, it looks to me at first glance like this doesn't really need to be a macro, but could be a higher-order function instead: take in a schema or whatever, and return a function of data. My guess is you're doing it as a macro for performance, in which case I would counsel you to try it out the slow, easy way first; once you have that working you can make it a macro if necessary. Or, maybe I'm wrong and there's something in here that fundamentally has to be a macro.
I want to transfer code from
("AAA" ("BB" 11 #"XXX"))
to
("AAA" ("BB" 11 "YYY"))
I just want to change #"XXX" to "YYY".
I write a function it works.
(defn tt [clause]
(cond (not (sequential? clause)) clause
(and (sequential? clause)
(= 2 (count clause))
(= `deref (first clause))
(string? (second clause)))
"YYY"
:else (map tt clause)))
there is my result:
(tt '("AAA" ("BB" 11 #"XXX"))) --> ("AAA" ("BB" 11 "YYY"))
But when I change the function to macro , it raise exception.
(defmacro test [& clause]
(let [f (fn tt [clause]
(cond (not (sequential? clause)) clause
(and (sequential? clause)
(= 2 (count clause))
(= `deref (first clause))
(string? (second clause)))
"YYY"
:else (map tt clause)))]
(f clause)))
and it raise exception like this
(test "AAA" ("BB" 11 #"XXX")) --> ClassCastException java.lang.String cannot be cast to clojure.lang.IFn
I have test map for prewalk function. Both of them raise exception.
I have no idea what is wrong with it and how to fix this error in macro??