I am going through some Clojure tutorials using Closure Box, and entered the following code:
user> (def stooges (vector "Moe" "Larry" "Curly"))
#'user/stooges
user> (contains? stooges "Moe")
false
Shouldn't this evaluate to TRUE ? Any help is appreciated.
This is a common trap! I remember falling into this one when I was getting started with Clojure :-)
contains? checks whether the index (0, 1, 2, etc.) exists in the collection.
You probably want something like:
(some #{"Moe"} stooges)
=> "Moe" <counts as boolean true>
(some #{"Fred"} stooges)
=> nil <counts as boolean false>
Or you can define your own version, something like:
(defn contains-value? [element coll]
(boolean (some #(= element %) coll)))
(contains-value? "Moe" stooges)
=> true
A vector is similar to an array. contains? returns true if the key exists in the collection. You should be looking for the "key/index" 0, 1 or 2
user=> (def stooges (vector "Moe" "Larry" "Curly"))
#'user/stooges
user=> (contains? stooges 1)
true
user=> (contains? stooges 5)
false
If you were using a hash...
user=> (def stooges {:moe "Moe" :larry "Larry" :curly "Curly"})
#'user/stooges
user=> (contains? stooges :moe)
true
user=> (contains? stooges :foo)
false
As mikera suggests, you probably want something like clojure.core/some
contains? support Set, if you use clojure-1.4
user=> (contains? #{:a, :b} :a)
true
user=> (contains? (set stooges) "Moe")
true
Related
I want to convert {a 1, b 2} clojure.lang.PersistentArrayMap into
[a 1 b 2] clojure.lang.PersistentVector in clojure.
I have tried to write a function in clojure which converts {a 1, b 2} into [a 1 b 2]. I have also written a macro which gives me expected end result. In clojure we cannot pass the values generated inside functions to macros. For that I wanted to know a way in which I can implement a macro directly which can convert {a 1, b 2} into (let [a 1 b 2] (println a)), which will return 1.
Dummy Macro:
(defmacro mymacro [binding & body]
--some implemetation---)
Execution :
(mymacro '{a 1, b 2} (println a))
output:
1
nil
My Implementaion:
Function which converts into desired output.
(defn myfn [x]
(let [a (into (vector) x) b (vec (mapcat vec a))] b))
Execution:
(myfn '{a 1, b 2})
Output:
[a 1 b 2]
MACRO:
(defmacro list-let [bindings & body] `(let ~(vec bindings) ~#body))
Execution:
(list-let [a 1 b 2] (println a))
Output:
1
nil
I wanted to know how can I implement the same inside the macro itself and avoid the function implementation to get the require output. Something same as dummy macro given above. I am also interested in knowing if there is any which through which I can pass the value from my funtion to the macro without using
(def)
in general, macro code is plain clojure code (with the difference that it returns clojure code to be evaluated later). So, almost anything you can think of coding in clojure, you can do inside macro to the arguments passed.
for example, here is the thing you're trying to do (if i understood you correctly):
(defmacro map-let [binding-map & body]
(let [b-vec (reduce into [] binding-map)]
`(let ~b-vec ~#body)))
(map-let {a 10 b 20}
(println a b)
(+ a b))
;;=> 10 20
30
or like this:
(defmacro map-let [binding-map & body]
`(let ~(reduce into [] binding-map) ~#body))
or even like this:
(defmacro map-let [binding-map & body]
`(let [~#(apply concat binding-map)] ~#body))
You don't need a macro for this, and you should always prefer functions over macros when possible.
For your particular case, I have already written a function keyvals which you may find handy:
(keyvals m)
"For any map m, returns the keys & values of m as a vector,
suitable for reconstructing via (apply hash-map (keyvals m))."
(keyvals {:a 1 :b 2})
;=> [:b 2 :a 1]
(apply hash-map (keyvals {:a 1 :b 2}))
;=> {:b 2, :a 1}
And, here are the full API docs.
If you are curious about the implementation, it is very simple:
(s/defn keyvals :- [s/Any]
"For any map m, returns the (alternating) keys & values of m as a vector, suitable for reconstructing m via
(apply hash-map (keyvals m)). (keyvals {:a 1 :b 2} => [:a 1 :b 2] "
[m :- tsk/Map ]
(reduce into [] (seq m)))
I know this is a recurring question (here, here, and more), and I know that the problem is related to creating lazy sequencies, but I can't see why it fails.
The problem: I had written a (not very nice) quicksort algorithm to sort strings that uses loop/recur. But applied to 10000 elements, I get a StackOverflowError:
(defn qsort [list]
(loop [[current & todo :as all] [list] sorted []]
(cond
(nil? current) sorted
(or (nil? (seq current)) (= (count current) 1)) (recur todo (concat sorted current))
:else (let [[pivot & rest] current
pred #(> (compare pivot %) 0)
lt (filter pred rest)
gte (remove pred rest)
work (list* lt [pivot] gte todo)]
(recur work sorted)))))
I used in this way:
(defn tlfnum [] (str/join (repeatedly 10 #(rand-int 10))))
(defn tlfbook [n] (repeatedly n #(tlfnum)))
(time (count (qsort (tlfbook 10000))))
And this is part of the stack trace:
[clojure.lang.LazySeq seq "LazySeq.java" 49]
[clojure.lang.RT seq "RT.java" 521]
[clojure.core$seq__4357 invokeStatic "core.clj" 137]
[clojure.core$concat$fn__4446 invoke "core.clj" 706]
[clojure.lang.LazySeq sval "LazySeq.java" 40]
[clojure.lang.LazySeq seq "LazySeq.java" 49]
[clojure.lang.RT seq "RT.java" 521]
[clojure.core$seq__4357 invokeStatic "core.clj" 137]]}
As far as I know, loop/recur performs tail call optimization, so no stack is used (is, in fact, an iterative process written using recursive syntax).
Reading other answers, and because of the stack trace, I see there's a problem with concat and adding a doall before concat solves the stack overflow problem. But... why?
Here's part of the code for the two-arity version of concat.
(defn concat [x y]
(lazy-seq
(let [s (seq x)]
,,,))
)
Notice that it uses two other functions, lazy-seq, and seq. lazy-seq is a bit like a lambda, it wraps some code without executing it yet. The code inside the lazy-seq block has to result in some kind of sequence value. When you call any sequence operation on the lazy-seq, then it will first evaluate the code ("realize" the lazy seq), and then perform the operation on the result.
(def lz (lazy-seq
(println "Realizing!")
'(1 2 3)))
(first lz)
;; prints "realizing"
;; => 1
Now try this:
(defn lazy-conj [xs x]
(lazy-seq
(println "Realizing" x)
(conj (seq xs) x)))
Notice that it's similar to concat, it calls seq on its first argument, and returns a lazy-seq
(def up-to-hundred
(reduce lazy-conj () (range 100)))
(first up-to-hundred)
;; prints "Realizing 99"
;; prints "Realizing 98"
;; prints "Realizing 97"
;; ...
;; => 99
Even though you asked for only the first element, it still ended up realizing the whole sequence. That's because realizing the outer "layer" results in calling seq on the next "layer", which realizes another lazy-seq, which again calls seq, etc. So it's a chain reaction that realizes everything, and each step consumes a stack frame.
(def up-to-ten-thousand
(reduce lazy-conj () (range 10000)))
(first up-to-ten-thousand)
;;=> java.lang.StackOverflowError
You get the same problem when stacking concat calls. That's why for instance (reduce concat ,,,) is always a smell, instead you can use (apply concat ,,,) or (into () cat ,,,).
Other lazy operators like filter and map can exhibit the exact same problem. If you really have a lot of transformation steps over a sequence consider using transducers instead.
;; without transducers: many intermediate lazy seqs and deep call stacks
(->> my-seq
(map foo)
(filter bar)
(map baz)
,,,)
;; with transducers: seq processed in a single pass
(sequence (comp
(map foo)
(filter bar)
(map baz))
my-seq)
Arne had a good answer (and, in fact, I'd never noticed cat before!). If you want a simpler solution, you can use the glue function from the Tupelo library:
Gluing Together Like Collections
The concat function can sometimes have rather surprising results:
(concat {:a 1} {:b 2} {:c 3} )
;=> ( [:a 1] [:b 2] [:c 3] )
In this example, the user probably meant to merge the 3 maps into one. Instead, the three maps were mysteriously converted into length-2 vectors, which were then nested inside another sequence.
The conj function can also surprise the user:
(conj [1 2] [3 4] )
;=> [1 2 [3 4] ]
Here the user probably wanted to get [1 2 3 4] back, but instead got a nested vector by mistake.
Instead of having to wonder if the items to be combined will be merged, nested, or converted into another data type, we provide the glue function to always combine like collections together into a result collection of the same type:
; Glue together like collections:
(is (= (glue [ 1 2] '(3 4) [ 5 6] ) [ 1 2 3 4 5 6 ] )) ; all sequential (vectors & lists)
(is (= (glue {:a 1} {:b 2} {:c 3} ) {:a 1 :c 3 :b 2} )) ; all maps
(is (= (glue #{1 2} #{3 4} #{6 5} ) #{ 1 2 6 5 3 4 } )) ; all sets
(is (= (glue "I" " like " \a " nap!" ) "I like a nap!" )) ; all text (strings & chars)
; If you want to convert to a sorted set or map, just put an empty one first:
(is (= (glue (sorted-map) {:a 1} {:b 2} {:c 3}) {:a 1 :b 2 :c 3} ))
(is (= (glue (sorted-set) #{1 2} #{3 4} #{6 5}) #{ 1 2 3 4 5 6 } ))
An Exception will be thrown if the collections to be 'glued' are not all of the same type. The allowable input types are:
all sequential: any mix of lists & vectors (vector result)
all maps (sorted or not)
all sets (sorted or not)
all text: any mix of strings & characters (string result)
I put glue into your code instead of concat and still got a StackOverflowError. So, I also replaced the lazy filter and remove with eager versions keep-if and drop-if to get this result:
(defn qsort [list]
(loop [[current & todo :as all] [list] sorted []]
(cond
(nil? current) sorted
(or (nil? (seq current)) (= (count current) 1))
(recur todo (glue sorted current))
:else (let [[pivot & rest] current
pred #(> (compare pivot %) 0)
lt (keep-if pred rest)
gte (drop-if pred rest)
work (list* lt [pivot] gte todo)]
(recur work sorted)))))
(defn tlfnum [] (str/join (repeatedly 10 #(rand-int 10))))
(defn tlfbook [n] (repeatedly n #(tlfnum)))
(def result
(time (count (qsort (tlfbook 10000)))))
-------------------------------------
Clojure 1.8.0 Java 1.8.0_111
-------------------------------------
"Elapsed time: 1377.321118 msecs"
result => 10000
(conj coll x), for performance reasons, will add x to the end of some collections and to the beginning of other collections.
Is there a way to check whether the collection will be naturally add-at-end or add-at-beginning? I.e.
(conj-at-end? x) ; false for lists and true for vectors.
It is fairly easy to write my own, by checking if something is a vector or list, etc, but this is error prone in case there is a new collection added or something changes.
conj is a part of IPersistentCollection interface. This interface defines only three methods:
conj - adds new element;
empty - returns an empty collection of the same type;
equiv - performs equality checks.
So, there is nothing like conj-at-end?.
I don't think it could be implemented, either. For example, how would you define it for internally unsorted collections like hash-maps and hash-sets?
(conj #{1 2 3 4 5} 6)
; => #{1 4 6 3 2 5}
Does conj add to the beginning or the end of a collection?
The question only makes sense for a sequential collection, for which you can just try it:
Define
(defn conj-at-end? [coll]
(and (sequential? coll)
(= (conj (empty coll) 1 2) [1 2])))
Then
(conj-at-end? []) ;=> true
(conj-at-end? ()) ;=> false
and
(conj-at-end? #{}) ;=> false
We also need the corresponding
(defn conj-at-beginning? [coll]
(and (sequential? coll)
(= (conj (empty coll) 1 2) [2 1])))
Then
(conj-at-beginning? []) ;=> false
(conj-at-beginning? ()) ;=> true
and
(conj-at-beginning? #{}) ;=> false
Note that a set is neither conj-at-beginning? nor conj-at-end?.
This rather pat solution would be upset if, for example, clojure.lang.PersistentArrayMaps, produced by array-map,
were stable under conj, which they are not, and
elected to comply with the empty clojure.lang.Sequential interface,
tested for by sequential?, which they do not.
I'm trying to find matching key and value pairs from a map. I'm using the following code:
(defn matches? [m k v]
(let [val (k m)]
(= val v)))
my-demo.core=> (matches? {:a 1 :b 2} :b 2)
true
my-demo.core=> (matches? {:a 1 :b 2} :b 3)
false
Another approach using superset?:
my-demo.core=> (superset? #{:a 1 :b 3} #{:a 1})
true
my-demo.core=> (superset? #{:a 1 :b 3} #{:a 2})
false
I have a feeling there is a better way to do this.
My question is: Is there an idiomatic way to find matching key and value in map in Clojure?
This is probably a small enough problem that you could just use this instead of defining a function:
(= ({:a 1 :b 2} :a)
1)
=> true
I would say this is an idiomatic way, which will work fine for most use cases.
However, it depends on the behaviour you require when testing for a nil value. because the above method would return true for :c nil:
(= ({:a 1 :b 2} :c)
nil)
=> true
And your function behaves the same way:
(matches? {:a 1 :b 2} :c nil)
=> true
To get around this you could use get with a "not found" value:
(= (get {:a 1 :b 2} :c ::not-found)
nil)
=> false
This works fine but it's perhaps not as neat. You'd just have to make sure that your "not found" value was never the same as your test value.
If you wanted to really know that a map contains a key with a possibly nil value you would instead have to check both things. Here's a function that would do this while only doing the hash-map lookup once. It uses (find map key) which returns the map entry (the key-value pair) for key, or nil if the key is not present.
(defn contains-kv? [m k v]
(if-let [kv (find m k)]
(= (val kv) v)
false))
(contains-kv? {:a 1 :b nil} :a 1)
=> true
(contains-kv? {:a 1 :b nil} :b nil)
=> true
(contains-kv? {:a 1 :b nil} :c nil)
=> false
Note: I don't think superset? is doing what you think it does. In that example you're using sets, not hash maps, which are completely different:
(clojure.set/superset? #{:a 1 :b 2} #{:a :b})
=> true
Your matches? function looks good to me, though I'd probably remove the let in this case, as it removes a bit of clutter. I'd also rename it to something more precise, though this is the best I can come up with just now:
(defn contains-kv?
"Returns true if the key k is present in the given map m and it's value matches v."
[m k v]
(= (m k) v))
Here is what I have:
(def my-atom (atom []))
(defn add-to-my-atom! [x]
(swap! my-atom conj x))
How do I append value to the vector only if it's not present already? I want to be able to use a predicate for the testing. For example in Common Lisp there is pushnew:
pushnew item place &key key test test-not
Is there something similar in Clojure? Perhaps, I should use sets instead of vectors. Fine. How do you define predicate that set will use to compare its values? For example, set can contain strings, and suppose that differences in case should not affect operations on sets, how Clojure deals with that?
Using a vector:
user> (def my-atom (atom []))
(defn push-new
[place item pred]
(if (pred place item)
(conj place item)
place))
(defn add-to-my-atom!
[x]
(swap! my-atom push-new x
(fn [place item]
(not (some #(= (.toLowerCase %)
(.toLowerCase item))
place)))))
#'user/add-to-my-atom!
user> (add-to-my-atom! "Hello World!")
["Hello World!"]
user> (add-to-my-atom! "hello world!")
["Hello World!"]
user> (add-to-my-atom! "ABCDE")
["Hello World!" "ABCDE"]
user> (add-to-my-atom! "abcde")
["Hello World!" "ABCDE"]
Using a set with a custom sorting comparitor:
user> (def my-atom (atom (sorted-set-by (fn [a b] (compare (.toLowerCase a) (.toLowerCase b))))))
#'user/my-atom
user> (swap! my-atom conj "Hello")
#{"Hello"}
user> (swap! my-atom conj "hello")
#{"Hello"}
user> (swap! my-atom conj "abc")
#{"abc" "Hello"}
user> (swap! my-atom conj "Abc")
#{"abc" "Hello"}
If you were not working with atoms, the fn to add to a vector if it is not there would be:
(defn push-new [v value]
(if (some #{value} v)
v
(conj v value)))
Now you can easily use that fn to move from one value of the atom to the next:
(defn add-to-my-atom [the-atom x]
(swap! the-atom push-new x))
Depending on your use case, a set can be more appropriate. Clojure relies on the equals and hashcode implementation of the objects that you put into the set, unless you use a sorted-set-by, or you can simply uppercase them before putting them into the set.