I have two sequences for comparison and I need to keep comparison results in a map, with data in the first sequence serving as keys and second as vals. Here comes sample code that works
(def myAtom (atom {}))
(map #(if (== %1 %2) (swap! myAtom assoc %1 %2 ))
[1 2 3] [4 5 3])
(prn #myAtom) ; ==> {3 3}
However after putting "same" thing above into one let bindings, it doesn't work anymore
(let [ myAtom (atom {})]
(map #(if (== %1 %2) (swap! myAtom assoc %1 %2 ))
[1 2 3] [4 5 3])
(prn #myAtom)) ;;==> {} empty???
So the question is, what happens to myAtom inside let binding? How come is it missing?
map is to generate lazy sequence out of a sequence whereas what you need is to do certain operation (i.e swap) for each item in a sequence, hence you need to use doseq
EDIT: (updated as par #mobyte suggestion)
(let [myAtom (atom {})
a [1 2 3]
b [4 5 3]]
(doseq [[x y] (map vector a b)]
(if (== x y) (swap! myAtom assoc x y )))
(prn #myAtom))
Your first example works because you executed each expression in REPL which made the map operation to execute its lazy operations.
I have seen many people try to use map to do certain operations like this, map should only be used for one purpose i.e mapping a sequence to another sequence without any side-effect operations.
As Ankur mentioned it's better to use doseq for imperative operations:
(let [myAtom (atom {})]
(doseq [[a b] (map vector
[1 2 3]
[4 5 3])]
(when (== a b) (swap! myAtom assoc a b )))
(prn #myAtom))
But you can force evaluation of the map result by using dorun in your original version:
(let [ myAtom (atom {})]
(dorun (map #(when (== %1 %2) (swap! myAtom assoc %1 %2 ))
[1 2 3] [4 5 3]))
(prn #myAtom))
P.S. Ankur's version is not equivalent to original one
doseq:
(doseq [a [1 2 3]
b [4 5 3]]
(println [a b]))
=> [1 4]
[1 5]
[1 3]
[2 4]
[2 5]
[2 3]
[3 4]
[3 5]
[3 3]
map:
(dorun (map #(println [%1 %2])
[1 2 3]
[4 5 3]))
=> [1 4]
[2 5]
[3 3]
Related
I'm trying to solve the square sum problem, more specifically creating a function that can help with the relations.
"the function should, given a set A of integers, produce a relation that includes tuple [a b],
iff a and b are in A and their sum is a square number"
To my aid, I got a help function square?, that tests whether a passed integer is a squared number.
(defn square?
[n]
(= n (sqr (isqrt n)))
)
I took a shot in dark with this, but honestly I'm lost.
(defn sqr-sum-rel
[A]
(reduce (fn [a b] (
if (square? (a + b))
[a b]
)) A )
)
Testing the function should return something like this:
(test? "test" (sqr-sum-rel (set (range 1 11))) #{[5 4] [2 2] [8 8] [4 5] [7 9] [1 3] [3 6] [6 10] [2 7] [1 8] [8 1] [7 2] [10 6] [6 3] [3 1] [9 7]})
I'm really new to Clojure and even functional programming in general, so any pointers in the right direction are appreciated.
To solve this problem, you can first generate all possible pairs of numbers from the set using for and then filter those pair. Assuming square? is properly implemented, we write a helper function that will be the first argument that we pass to filter:
(defn sum-is-square? [coll]
(square? (apply + coll)))
It takes a collection of numbers as argument and test if their sum is square?. Then we can write the complete function like
(defn sqr-sum-rel [A]
(set (filter sum-is-square? (for [a A
b A]
[a b]))))
that takes as argument a set A and first produces all pairs that can be formed by elements from this set, and then only keep those pairs for which sum-is-square? evaluates to true.
We can test it by passing in a set of numbers:
(sqr-sum-rel (set (range 1 11)))
;; => #{[8 8] [2 2] [7 2] [5 4] [6 3] [1 3] [1 8] [8 1] [7 9] [2 7] [3 6] [10 6] [4 5] [9 7] [3 1] [6 10]}
Added: A briefer implementation as suggested by the original poster uses the :when option of for:
(defn sqr-sum-rel [A]
(set (for [a A
b A :when (square? (+ a b))]
[a b]))))
i would also guess that the the number in set can't form the pair with itself, and also there is no need to compare pair members in reverse order, since we already know they form the relation needed. I would update it like this:
(defn sq-rel [a-set]
(seq (for [[x & t] (take-while seq (iterate rest a-set))
y t
:when (square? (+ x y))]
[x y])))
this one is more or less like an imperative
for (i = 0; i < len; i++) {
for (j = i + 1; j < len; j++)
if (isSquare(a[i] + a[j])) {
res.push([a[i], a[j]])
}
}
}
in repl:
user> (sq-rel #{1 2 3 4 5 6})
;;=> ([1 3] [4 5] [6 3])
I am a newbie to clojure. One question I am facing is to find special lists from multiple collections.
list one: (1 2 3 4 5)
list two: (a b c d e f)
Source list: (1 c 2 b 4 e a f)
Lists to find in the source list: (a 1), (1 a), (4 c), (f 2)
How to resolve it?
Thanks
Have a look at https://github.com/clojure/math.combinatorics
=> (combo/cartesian-product [1 2] [3 4])
((1 3) (1 4) (2 3) (2 4))
If my understanding of the problem is correct and you don't mind brute-force, you can use this solution.
(let [list1 '#{1 2 3 4 5}
list2 '#{a b c d e f}
source '(1 c 2 b 4 e a f)]
(for [x source
y source
:when (or (and (list1 x) (list2 y))
(and (list1 y) (list2 x)))]
[x y]))
=>
([1 c] [1 b] [1 e] [1 a] [1 f] [c 1] [c 2] [c 4] [2 c] [2 b] [2 e] [2 a] [2 f] [b 1] [b 2] [b 4] [4 c] [4 b] [4 e] [4 a] [4 f] [e 1] [e 2] [e 4] [a 1] [a 2] [a 4] [f 1] [f 2] [f 4])
You can also use shuffle to introduce some randomness and use take n to acquire the samples you need.
Or to make it even simpler, just remove items from list1 and list2 not in source and loop through these filtered lists.
I've an algorithm as follows -
(defn max-of
[args]
(into [] (apply map #(apply max %&) args)))
which works fine.
(max-of [[1 7] [3 5] [7 9] [2 2]]) returns [7 9]
It basically finds the maximum element on each position. 7 is the largest first element is the collection and 9 is the largest second element. However, when trying to use reducer/map from core.reducers, i get
CompilerException clojure.lang.ArityException: Wrong number of args (21) passed to: reducers/map
So this does not work -
(defn max-of
[args]
(into [] (apply r/map #(apply max %&) args)))
Why?
UPDATE
my final code is
(defn max-of [[tuple & tuples]]
(into [] (r/fold (fn
([] tuple)
([t1 t2] (map max t1 t2)))
(vec tuples))))
Running a quick bench on it gives Execution time mean : 626.125215 ms
I've got this other algorithm that I wrote before -
(defn max-fold
[seq-arg]
(loop [acc (transient []) t seq-arg]
(if (empty? (first t))
(rseq (persistent! acc))
(recur (conj! acc (apply max (map peek t))) (map pop t)))))
which does that same thing. For this i got - Execution time mean : 308.200310 ms which is twice as fast than the r/fold parallel thingy. Any ideas why?
Btw, if I remove into [] from the r/fold stuff, then I get Execution time mean : 13.101313 ms.
r/map takes [f] or [f coll] - so your apply approach won't work here
user=> (doc r/map)
-------------------------
clojure.core.reducers/map
([f] [f coll])
vs.
user=> (doc map)
-------------------------
clojure.core/map
([f] [f coll] [f c1 c2] [f c1 c2 c3] [f c1 c2 c3 & colls])
The answer to the question why has already been given. So let's answer the next question: "what are you trying to do?"
According to how i've understood your goal (find maximum elements by the position in tuples) and to do it potentially in parallel (as you are trying to use reducers), that's what you have to do
(defn max-of [tuples]
(r/fold (fn
([] (first tuples))
([t1 t2] (map max t1 t2)))
((rest tuples)))
user> (max-of [[1 7] [3 5] [7 9] [2 2]])
(7 9)
(max-of [[1 2 3] [3 2 1] [4 0 4]])
(4 2 4)
user> (max-of [])
nil
user> (max-of [[1 2 3]])
[1 2 3]
or even better with destructuring:
(defn max-of [[tuple & tuples]]
(r/fold (fn
([] tuple)
([t1 t2] (map max t1 t2)))
tuples))
update:
for large data you should optimize it and switch to using vectors
(defn max-of [[tuple & tuples]]
(r/fold (fn
([] tuple)
([t1 t2] (map max t1 t2)))
(vec tuples)))
user> (max-of (repeat 1000000 [1 2 3 4 5 6 7 8 9 10]))
(1 2 3 4 5 6 7 8 9 10)
I am trying to write a function in clojure returns cartesian product
(my-compute-fn [1 2 3] [4 5 6])
will return
[[1 4] [1 5] [1 6] [2 4] [2 5] ....]
My attempt resulted in this
(defn compute [col1 col2]
(let [totcol2 (count col2)
totcol1 (count col2)]
(map #(vector %1 %2)
(mapcat #(repeat totcol1 %1) col1)
(take (* totcol1 totcol2) (cycle col2)))))
Which does the job, but looks a bit clunky.
What would be a better and more concise implementation, involving more readily available functions from clojure.core?
Thanks!
edit:
Found clojure combinatorics, and it doesn't seem there's a magic functional way of doing this.
for is the prime candidate for this task:
(for [a [1 2 3]
b [4 5 6]]
[a b])
;; => ([1 4] [1 5] [1 6] [2 4] [2 5] [2 6] [3 4] [3 5] [3 6])
You can supply for with any number of seqs and it will bind the given symbols to each element of the seq associated with them. There is even cooler stuff like :when that lets you e.g. find all those products whose sum is 7:
(for [a [1 2 3]
b [4 5 6]
:when (= (+ a b) 7)]
[a b])
;; => ([1 6] [2 5] [3 4])
Let's say i have the following vector
(def x [[1 2 3] [4 5 6] [7 8]])
and I want to append the number 9 to the last vector (I don't know the index of the vector)
(conj (vec (butlast x)) (conj (last x) 9))
#=> [[1 2 3] [4 5 6] [7 8 9]]
Is there a better/clearer way to do this?
Use the efficient tail access functions
(conj (pop x) (conj (peek x) 9))
But you could also
(update-in x [(dec (count x))] conj 9)