why Clojure conatins? behave so strangely? [duplicate] - clojure

This question already has answers here:
Issue with Clojure 'contains'
(3 answers)
Closed 1 year ago.
Why clojure output true fo first one and false for the second one
(def myset [3 5 7 11 13 17 19])
(defn check-n
[n]
(contains? myset n))
(check-n 1)
(check-n 20)

contains? is function for checking keys in collection. It can be used with map:
(contains? {:a 1 :b 2} :a)
=> true
(contains? {:a 1 :b 2} :c)
=> false
or with vector- in this case, it checks whether vector contains given index:
(contains? [1 2 3] 0)
=> true
(contains? [1 2 3] 3)
=> false
If you want to check occurence of number, use .contains from Java:
(.contains [1 2 3] 3)
=> true
or some with set used as predicate:
(some #{3} [1 2 3])
=> 3
(some #{4} [1 2 3])
=> nil

Related

What is the difference between set and hash-set in Clojure?

I can't find an explanation on the documentation nor on the web for why there are two different functions that seem to do practically the same thing, apart from accepting one a collection and the other one a list of arguments (but this could be easily solved using (apply hash-set coll)).
Just checked the source code for set and hash-set. You are right that there is practically no difference, aside from one accepting multiple arguments and the other accepting a collection.
Here is the source, by the way:
For set
For hash-set
It is just for convenience. Same with vector vs vec. However it is not completely parallel for maps and lists:
(vector 0 1 2) => [0 1 2]
(apply vector (range 3)) => [0 1 2]
(vec (range 3)) => [0 1 2]
(hash-set 0 1 2) => #{0 1 2}
(apply hash-set (range 3)) => #{0 1 2}
(set (range 3)) => #{0 1 2}
(hash-map :a 1 :b 2) => {:b 2, :a 1}
(apply hash-map [:a 1 :b 2]) => {:b 2, :a 1}
(into {} [[:a 1] [:b 2]]) => {:a 1, :b 2}
(list 0 1 2) => (0 1 2)
(apply list (range 3)) => (0 1 2)
(into (list) (range 3)) => (2 1 0) ; *** reversed order ***
Since we can define each in terms of the other:
(defn hash-set [& args]
(clojure.core/set args))
or
(defn set [coll]
(apply clojure.core/hash-set coll))
... it is likely that both are defined separately for speed.

Composing a small list with elements among some candidate elements, where the inclusion of each element depends on an independent predicate?

Say I want to return a collection containing some subset of [1 2 3 4]. Whether 1 is included depends on w?, whether 2 is included depends on x?, 3 on y?, 4 on z?.
If I did
[(if (w?) 1) (if (x?) 2) (if (y?) 3) (if (z?) 4)]
And (x?) and (y?) evaluated to true, the others false, I'd end up with
[nil 2 3 nil]
but I want
[2 3]
Is there a canonical way to accomplish this? It's reminiscent of a list comprehension, but not quite the same.
Thanks
EDIT:
I supposed I could create a map {1 w? 2 x? 3 y? z 4?} and then reduce that by either consing or not consing each key depending on whether it's value function evaluates to true, but maybe there's a better way.
This would do what you want:
(defn f [conds vs]
(when (seq vs)
(let [[c1 & cr] conds
[v1 & vr] vs]
(if c1
(cons v1 (f cr vr))
(f cr vr)))))
Example:
(f [false true true false] [1 2 3 4])
=> (2 3)
(f [true true false true false true] [1 2 3 nil 4 5])
=> (1 2 nil 5)

Clojure: how to test if a seq is a "subseq" of another seq

Is there an easy / idiomatic way in Clojure to test whether a given sequence is included within another sequence? Something like:
(subseq? [4 5 6] (range 10)) ;=> true
(subseq? [4 6 5] (range 10)) ;=> false
(subseq? "hound" "greyhound") ;=> true
(where subseq? is a theoretical function that would do what I'm describing)
It seems that there is no such function in the core or other Clojure libraries... assuming that's true, is there a relatively simple way to implement such a function?
(defn subseq? [a b]
(some #{a} (partition (count a) 1 b)))
(defn subseq? [target source]
(pos? (java.util.Collections/indexOfSubList (seq source) (seq target))))
***
DISCLAIMER EDIT
This proposal is not reliable for reasons discussed in comments section.
***
#amalloy 's solution has one flaw - it won't work with infinite lazy sequences. So it will loop forever when you run this:
(subseq? [1 2 3] (cycle [2 3 1]))
I propose this implementation to fix this:
(defn- safe-b
"In case b is a cycle, take only two full cycles -1 of a-count
to prevent infinite loops yet still guarantee finding potential solution."
[b a-count]
(take
(* 2 a-count)
b))
(defn subseq? [a b]
(let [a-count (count a)]
(some #{a} (partition a-count 1 (safe-b b a-count)))))
=> #'user/safe-b
=> #'user/subseq?
(subseq? [1 2 3] (cycle [2 3 1]))
=> [1 2 3]
(subseq? [1 2 3] (cycle [3 2 1]))
=> nil
(subseq? [1 2 3] [2 3])
=> nil
(subseq? [2 3] [1 2 3])
=> [2 3]

Clojure equality of collections with sequences

I noticed that Clojure (1.4) seems to be happy to consider vectors equal to the seq of the same vector, but that the same does not apply for maps:
(= [1 2] (seq [1 2]))
=> true
(= {1 2} (seq {1 2}))
=> false
Why should the behaviour of = be different in this way?
Clojure's = can be thought of as performing its comparisons in two steps:
Check if the types of the things being compared belong to the same "equality partition", that is a class of types whose members might potentially be equal (depending on things like the exact members of a given data structure, but not the particular type in the partition);
If so, check if the things being compared actually are equal.
One such equality partition is that of "sequential" things. Vectors are considered sequential:
(instance? clojure.lang.Sequential [])
;= true
As are seqs of various types:
(instance? clojure.lang.Sequential (seq {1 2}))
;= true
Therefore a vector is considered equal to a seq if (and only if) their corresponding elements are equal.
(Note that (seq {}) produces nil, which is not sequential and compares "not equal" to (), [] etc.)
On the other hand, maps constitute an equality partition of their own, so while a hash map might be considered equal to a sorted map, it will never be considered equal to a seq. In particular, it is not equal to the seq of its entries, which is what (seq some-map) produces.
I guess this is because in sequences order as well as value at particular position matters where as in map the order of key/value doesn't matter and this difference between semantics causes this to work as shown by your sample code.
For more details have a look at mapEquals in file https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/APersistentMap.java
It checks if the other object is not map then return false.
user=> (seq {1 2})
([1 2])
user=> (type {1 2})
clojure.lang.PersistentArrayMap
It seems to me that this example points out a slight inconsistency in the notion of equality of values in clojure for this case where they are different types derived from the same type (by the seq function). It could well be argued that this is not inconsistent because it is comparing a derived type to the type it is derived from and I can understand that if the same logic was applied to this same example using vectors (note at the bottom)
the contents are the same type:
user> (type (first (seq {1 2})))
clojure.lang.MapEntry
user> (type (first {1 2}))
clojure.lang.MapEntry
user> (= (type (first {1 2})) (type (first (seq {1 2}))))
true
user> (= (first {1 2}) (first (seq {1 2})))
true
the sequences have the same values
user> (map = (seq {1 2}) {1 2})
(true)
but they are not considered equal
user> (= {1 2} (seq {1 2}))
false
this is true for longer maps as well:
user> (map = (seq {1 2 3 4}) {1 2 3 4})
(true true)
user> (map = (seq {1 2 3 4 5 6}) {1 2 3 4 5 6})
(true true true)
user> (map = (seq {9 10 1 2 3 4 5 6}) {9 10 1 2 3 4 5 6})
(true true true true)
even if they are not in the same order:
user> (map = (seq {9 10 1 2 3 4 5 6}) {1 2 3 4 5 6 9 10})
(true true true true)
but again not if the containing types differ :-(
user> (= {1 2 3 4} (seq {1 2 3 4}))
false
EDIT: this is not always true see below:
to work around this you can convert everything to a seq before comparison, which is (I presume) safe because the seq function always iterates the whole data structure the same way and the structures are immutable values and a seq of a seq is a seq
user> (= (seq {9 10 1 2 3 4 5 6}) {1 2 3 4 5 6 9 10})
false
user> (= (seq {9 10 1 2 3 4 5 6}) (seq {1 2 3 4 5 6 9 10}))
true
vectors are treated differently:
user> (= [1 2 3 4] (seq [1 2 3 4]))
true
Perhaps understanding the minor inconsistencies is part of learning a language or someday this could be changed (though I would not hold my breath)
EDIT:
I found two maps that produce different sequences for the same value so just calling seq on the maps will not give you proper map equality:
user> (seq (zipmap [3 1 5 9][4 2 6 10]))
([9 10] [5 6] [1 2] [3 4])
user> (seq {9 10 5 6 1 2 3 4})
([1 2] [3 4] [5 6] [9 10])
user>
here is an example of what I'm calling proper map equality:
user> (def a (zipmap [3 1 5 9][4 2 6 10]))
#'user/a
user> (def b {9 10 5 6 1 2 3 4})
#'user/b
user> (every? true? (map #(= (a %) (b %)) (keys a)))
true
(seq some-hash-map) gives you a sequence of entries (key/value pairs).
For example:
foo.core=> (seq {:a 1 :b 2 :c 3})
([:a 1] [:c 3] [:b 2])
which is not the same as [:a 1 :b 2 :c 3].

Compare two vectors in clojure no matter the order of the items

I want to compare two vectors and find out if the items they have are the same no matter the order the items are in.
So..
right now in clojure:
(= [1 2 3] [3 2 1]) ;=> false
I want:
(other_fun [1 2 3] [3 2 1]) ;=> true
(other_fun [1 2 3 4] [3 2 1]) ;=> false
I could not find a containsAll like in java
If you do care about duplicates, you can compare their frequency maps. These are maps with each collection element as a key and number of occurrences as a value. You create them using standard function frequencies, like in given examples.
Different order, same number of duplicates:
(= (frequencies [1 1 2 3 4])(frequencies [4 1 1 2 3]))
evaluates true.
Different order, different number of duplicates:
(= (frequencies [1 1 2 3 4])(frequencies [4 1 2 3]))
evaluates false.
So, you can write a function:
(defn other_fun [& colls]
(apply = (map frequencies colls)))
If you don't care about duplicates, you could create sets from both vectors and compare these:
(= (set [1 2 3]) (set [3 2 1])) ;=> true
As a function:
(defn set= [& vectors] (apply = (map set vectors)))
If you don't care about duplicates, other answers a perfectly applicable and efficient.
But if you do care about duplicates, probably the easiest way to compare two vectors is sorting and comparing:
user=> (= (sort [3 5 2 2]) (sort [2 2 5 3]))
true
user=> (= (sort [3 5 2 2]) (sort [2 5 3]))
false
Create sets from them:
user=> (= (set [1 2 3]) (set [3 2 1]))
true
user=> (defn other_func [col1 col2]
(= (set col1) (set col2)))
#'user/other_func
user=> (other_func [1 2 3] [3 2 1])
true
You're on the JVM already, so if you want containsAll, then just use containsAll, right?
(defn other_fun
"checkes the presence of the elements of vec1 in vec2 and vice versa"
[vec1 vec2]
(if (or (some nil?
(for [a vec1 b [vec2]] (some #(= % a) b)))
(some nil?
(for [a vec2 b [vec1]] (some #(= % a) b))))
false
true))
(other_fun [1 2 3] [3 2 1]) ;=> true
(other_fun [1 2 3 4] [3 2 1]) ;=> false