Clojure: Cycle through a collection while looping through another collection? - clojure

I have two collections x and y, both having different number of items. I want to loop through x and do something with a side-effect, while cycling through y. I don't want to repeat y while looping through x. Both doseq and for repeats y:
(for [x (range 5)
y ["A" "B"]]
[x y])
This produces ([0 "A"] [0 "B"] [1 "A"] [1 "B"] [2 "A"] [2 "B"] [3 "A"] [3 "B"] [4 "A"] [4 "B"]).
What I want is something that will produce: ([0 "A"] [1 "B"] [2 "A"] [3 "B"] [4 "A"]).
Background, I have lines from a file and core.async channels (say 5) and I want to put each line to the next channel in my collection, something like:
(defn load-data
[file chans]
(with-open [rdr (io/reader file)]
(go
(doseq [l (line-seq rdr)
ch chans]
(>! ch l)))))

If you pass multiple sequences to map it steps through each of them in lock step calling the mapped function with the value from the current position in each. Stopping when one of the sequences runs out.
user> (map vector (range 5) (cycle ["A" "B"]))
([0 "A"] [1 "B"] [2 "A"] [3 "B"] [4 "A"])
In this case the sequence from (cycle ["A" "B"]) will keep producing As and Bs forever though map will stop consuming them when the sequence from (range 5) ends. each step then calls the vector function with these two arguments and adds the result to the output sequence.
and for the second example using a go-loop is a fairly standard way of fanning out an input sequence:
user> (require '[clojure.core.async :refer [go go-loop <! <!! >!! >! chan close!]])
nil
user> (defn fanout [channels file-lines]
(go-loop [[ch & chans] (cycle channels)
[line & lines] file-lines]
(if line
(do
(>! ch line)
(recur chans lines))
(doseq [c channels]
(close! c)))))
#'user/fanout
user> (def lines ["first" "second" "third" "fourth" "fifth"])
#'user/lines
user> (def test-chans [(chan) (chan) (chan)])
#'user/test-chans
user> (fanout test-chans lines)
#<ManyToManyChannel clojure.core.async.impl.channels.ManyToManyChannel#3b363fc5>
user> (map <!! test-chans)
("first" "second" "third")
user> (map <!! test-chans)
("fourth" "fifth" nil)

Related

Extracting two map elements with the largest distance in Clojure

I am trying to extract two elements of a map with the largest distance. For that, I defined the function for calculating the distance and can obtain the distance between the first element (p1) and other elements of the map. But I need to calculate distances between the second item (p2) and the next ones (p3, p4, p5), the third item (p3) and (p4, p5), the fourth item (p4) and fifth item (p5). Then I need to identify the maximum amount between all distances and return the 2 items with the largest distance and the distance itself. Any help is highly appreciated.
Here is my code:
(defn eclid-dist
[u v]
(Math/sqrt (apply + (map #(* % %) (mapv - u v)))))
(def error
{:p1 [1 2 3]
:p2 [4 5 6]
:p3 [7 8 9]
:p4 [1 2 3]
:p5 [6 5 4]})
(dotimes [i (dec (count error))]
(let [dis (eclid-dist (second (nth (seq error) 0))
(second (nth (seq error) (+ i 1))))
max-error (max dis)]
(println [':dis' dis ':max-error' max-error])))
I tried to save each calculated distance as a vector element separately to prevent overwriting but it was not successful.
You could use the for macro for this. It let's you combine two nested loops to test for all pairs. Then you can use max-key to pick the pair with largest distance:
(defn find-largest-dist-pair [vec-map]
(apply max-key second
(for [[[k0 v0] & r] (iterate rest vec-map)
:while r
[k1 v1] r]
[[k0 k1] (eclid-dist v0 v1)])))
(find-largest-dist-pair error)
;; => [[:p3 :p4] 10.392304845413264]
There is nothing wrong with eclid-dist, you could just use the dedicated Clojure library clojure.math (and ->> thread-last macro for better readability) and rewrite it like this:
(:require [clojure.math :as m])
(defn distance [u v]
(->> (mapv - u v)
(mapv #(m/pow % 2))
(reduce +)
m/sqrt))
Your main problem is, how to create unique pairs of points from your data. You could write a recursive function for this:
(defn unique-pairs [point-seq]
(let [[f & r] point-seq]
(when (seq r)
(concat (map #(vector f %) r)
(unique-pairs r)))))
(def error {:p1 [1 2 3]
:p2 [4 5 6]
:p3 [7 8 9]
:p4 [1 2 3]
:p5 [6 5 4]})
(unique-pairs (vals error))
or use library clojure.math.combinatorics:
Dependency: [org.clojure/math.combinatorics "0.1.6"]
(:require [clojure.math.combinatorics :as combi])
(combi/combinations (vals error) 2)
Note that these functions have slightly different results- it doesn't affect the final result, but if you can, you should use combinations.
Now, you have to compute distance for all these pairs and return the pair with the largest one:
(defn max-distance [point-map]
(->> (combi/combinations (vals point-map) 2)
(map (fn [[u v]] {:u u :v v :distance (distance u v)}))
(apply max-key :distance)))
(max-distance error)
=> {:u [1 2 3], :v [7 8 9], :distance 10.392304845413264}

How do I get Clojure maro to output the original code without substitutions?

There is macros. This macro is convenient because it allows you
to see the result of executing the Clojure script.
But often it gives out a result that does not suit me.
I use Babashka for running scripts.
(defn symbol-several
"returns a symbol with the concatenation of the str values of the args"
[& x]
(symbol (apply str x)))
(defmacro ! [& forms]
(cons
`symbol-several
(for [form forms]
`(str '~form " ;-> " ~form "\n"))))
,that outputs:
c:\clj>bb "~#.clj"
(do (def v [3 4]) (def l (quote (1 2))) (clojure.core/sequence (clojure.core/seq (clojure.core/concat (clojure.core/list 0) l v)))) ;-> (0 1 2 3 4)
The ~#.clj file contains the macro "!" and the code:
"(! (do (def v [3 4]) (def l '(1 2)) `(0 ~#l ~#v)))"
How to rewrite a macro so that it outputs the original code, i.e. something like this:
(do (def v [3 4]) (def l '(1 2)) `(0 ~#l ~#v))) ;-> (0 1 2 3 4)
Also, instead of the result (list), the macro outputs LazySeq:
c:\clj>bb map.clj
(apply map vector [[1 2] [3 4]]) ;-> clojure.lang.LazySeq#8041
I use Babashka (bb.exe) for running script.
The map.clj file contains the macro "!" and the code:
(apply map vector [[1 2] [3 4]])
Using pr-str helps printing Clojure data structures properly.
(defn symbol-several
"returns a symbol with the concatenation of the str values of the args"
[& x]
(symbol (apply str x)))
(defmacro ! [& forms]
`(symbol-several
~#(for [form forms]
`(str '~form " ;-> " (pr-str ~form) "\n"))))
(! (apply map vector [[1 2] [3 4]]))
;; => (apply map vector [[1 2] [3 4]]) ;-> ([1 3] [2 4])
Also see https://clojuredocs.org/clojure.core/pr-str

Clojure getting a coordinate from arrays

I'm having trouble figuring out how I can find a coordinate of a character within an array.
I have small ASCII maps that look like this:
+-----+
|XPX X|
|X X|
|X XX|
|X X|
|XX DX|
+-----+
This map is split up by lines into an array so the array looks like:
["+-----+" "|XPX X|" "|X X|" "|X XX|" "|X X|" "|XX DX|" "+-----+"]
What I am trying to do is pass a character such as 'P' and find out the coordinate of the character in the map. In this instance 'P' is located at the coordinate (2,3).
As a starting point, I tried to figure out the column of the character using
(doseq [m map] (println (clojure.string/index-of m "P")))
This returned the output of
nil
2
nil
nil
nil
nil
nil
nil
At this point, I am now confused as to how I can just return '2' and the array index that this row is in.
One way to do it - assuming your input is a vector of strings as you have presented, is like so:
(def the-map ["+-----+" "|XPX X|" "|X X|" "|X XX|" "|X X|" "|XX DX|" "+-----+"])
(defn get-index [item coll]
(some #(when ((complement nil?) %) %)
(map-indexed (fn [ix x] (let [i (clojure.string/index-of x item)]
(when i [ix i])))
coll)))
This will return the first instance of whatever item you are looking for, e.g. for "+" it would be [0,0]. Also (2,3) is actually (1,2) as clojure uses zero-based indexing.
You can shorten this a little by using keep with identity
(defn get-index [item coll]
(first (keep identity
(map-indexed (fn [ix x] (let [i (clojure.string/index-of x item)]
(when i [ix i])))
coll))))
EDIT:
I realized it's redundant to have (first (keep identity....), instead just use some with identity:
(defn get-index [item coll]
(some identity
(map-indexed (fn [ix x] (let [i (clojure.string/index-of x item)]
(when i [ix i])))
coll)))
Some answers below gave you a way to get every matching coordinate, rather than the first. All you have to do is change some identity to remove nil? in my above version to accomplish this.
For example:
(defn get-index [item coll]
(remove nil?
(map-indexed (fn [ix x] (let [i (clojure.string/index-of x item)]
(when i [ix i])))
coll)))
Finally, if it is really your intention to have indexes be one-based, you can just increment each found index:
(defn get-index [item coll]
(some identity
(map-indexed (fn [ix x] (let [i (clojure.string/index-of x item)]
(when i [(inc ix) (inc i)])))
coll)))
(def strmap ["+-----+" "|XPX X|" "|X X|" "|X XX|" "|X X|" "|XX DX|" "+-----+"])
(defn find-char-2d
[arr char-to-find]
(some->> (map-indexed (fn [i row]
[i (clojure.string/index-of row char-to-find)])
arr)
(filter second)
first
(map inc)))
(println (find-char-2d strmap "Q")) ; prints "nil"
(println (find-char-2d strmap "P")) ; prints "(2, 3)"
The map-indexed loops through the row strings, searching each one for your substring while keeping track of the row index i. The some->> threading macro passes the result (which is a LazySeq) to the filter, which removes all elements with nil columns. Since we only want the first coordinates (assuming the character you're searching for can only exist once in the map), we select the first element. If the substring doesn't exist in the map, we will get nil, and the the some->> will short-circuit so that nil is returned. Otherwise, the indices will both be incremented (since your coordinates are 1-indexed.
Alternatively, you could linearize the your map, find the index in the linear map, and then calculate the 2d coordinates from the linear index:
(defn find-char-2d
[arr char-to-find]
(some-> (clojure.string/join "" arr)
(clojure.string/index-of char-to-find)
((juxt #(-> (/ % (count arr)) int inc)
#(inc (mod % (count (first arr))))))))
I went a little bit further and wrote a function that find coordinates of all the occurrences of a char.
(let [a ["+-----+" "|XPX X|" "|X X|" "|X XX|" "|X X|" "|XX DX|" "+-----+"]]
(letfn [(find-coords [a ch]
(let [op (fn [f] (comp inc #(f % (count (first a)))))]
(->> a
(clojure.string/join "")
(map-indexed vector)
(filter (comp (partial = ch) second))
(map first)
(map (juxt (op quot) (op rem))))))]
(find-coords a \P)))
=> ([2 3])
(find-coords a \X)
=> ([2 2] [2 4] [2 6] [3 2] [3 6] [4 2] [4 5] [4 6] [5 2] [5 6] [6 2] [6 3] [6 6])
I added another P char to show how to find all of them. Here is a simpler solution using for:
(defn strs->array [strs] (mapv vec strs))
(def data ["+-----+"
"|XPX X|"
"|X X|"
"|X XX|"
"|X P X|"
"|XX DX|"
"+-----+"])
(def data-array (strs->array data))
(defn find-chars-2 [ch-array tgt-char]
(let [num-rows (count ch-array)
num-cols (count (first ch-array))]
(for [ii (range num-rows)
jj (range num-cols)
:let [curr-char (get-in ch-array [ii jj])]
:when (= tgt-char curr-char)]
[ii jj])))
with result:
(find-chars-2 data-array \P)) => ([1 2] [4 3])
Using get-in assumes you have nested vectors, hence the need for strs->array. We also assume the data is rectangular (not ragged) and haven't put in any error-checking that a real solution would need.
i would go with something like this (a bit more generalized)
(def field ["+-----+"
"|XPX X|"
"|X ZX|"
"|X XX|"
"|X X|"
"|XX DX|"
"+-----+"])
(defn find-2d [pred field]
(for [[i line] (map-indexed vector field)
[j ch] (map-indexed vector line)
:when (pred ch)]
[i j ch]))
user> (find-2d #{\P} field)
;;=> ([1 2 \P])
user> (find-2d #{\P \Z} field)
;;=> ([1 2 \P] [2 4 \Z])
user> (find-2d #{\D \P \Z} field)
;;=> ([1 2 \P] [2 4 \Z] [5 4 \D])
user> (find-2d #(Character/isUpperCase %) field)
;;=> ([1 1 \X] [1 2 \P] [1 3 \X] [1 5 \X]
;; [2 1 \X] [2 4 \Z] [2 5 \X] [3 1 \X]
;; [3 4 \X] [3 5 \X] [4 1 \X] [4 5 \X]
;; [5 1 \X] [5 2 \X] [5 4 \D] [5 5 \X])
another one is more functional (although a less readable)
(defn find-2d [pred field]
(filter (comp pred last)
(mapcat #(map vector (repeat %1) (range) %2)
(range)
field)))
works exactly the same as the first one

Clojure - Function that returns all the indices of a vector of vectors

If I have a vector [[[1 2 3] [4 5 6] [7 8 9]] [[10 11] [12 13]] [[14] [15]]]
How can I return the positions of each element in the vector?
For example 1 has index [0 0 0], 2 has index [0 0 1], etc
I want something like
(some-fn [[[1 2 3] [4 5 6] [7 8 9]] [[10 11] [12 13]] [[14] [15]]] 1)
=> [0 0 0]
I know that if I have a vector [1 2 3 4], I can do (.indexOf [1 2 3 4] 1) => 0 but how can I extend this to vectors within vectors.
Thanks
and one more solution with zippers:
(require '[clojure.zip :as z])
(defn find-in-vec [x data]
(loop [curr (z/vector-zip data)]
(cond (z/end? curr) nil
(= x (z/node curr)) (let [path (rseq (conj (z/path curr) x))]
(reverse (map #(.indexOf %2 %1) path (rest path))))
:else (recur (z/next curr)))))
user> (find-in-vec 11 data)
(1 0 1)
user> (find-in-vec 12 data)
(1 1 0)
user> (find-in-vec 18 data)
nil
user> (find-in-vec 8 data)
(0 2 1)
the idea is to make a depth-first search for an item, and then reconstruct a path to it, indexing it.
Maybe something like this.
Unlike Asthor's answer it works for any nesting depth (until it runs out of stack). Their answer will give the indices of all items that match, while mine will return the first one. Which one you want depends on the specific use-case.
(defn indexed [coll]
(map-indexed vector coll))
(defn nested-index-of [coll target]
(letfn [(step [indices coll]
(reduce (fn [_ [i x]]
(if (sequential? x)
(when-let [result (step (conj indices i) x)]
(reduced result))
(when (= x target)
(reduced (conj indices i)))))
nil, (indexed coll)))]
(step [] coll)))
(def x [[[1 2 3] [4 5 6] [7 8 9]] [[10 11] [12 13]] [[14] [15]]])
(nested-index-of x 2) ;=> [0 0 1]
(nested-index-of x 15) ;=> [2 1 0]
Edit: Target never changes, so the inner step fn doesn't need it as an argument.
Edit 2: Cause I'm procrastinating here, and recursion is a nice puzzle, maybe you wanted the indices of all matches.
You can tweak my first function slightly to carry around an accumulator.
(defn nested-indices-of [coll target]
(letfn [(step [indices acc coll]
(reduce (fn [acc [i x]]
(if (sequential? x)
(step (conj indices i) acc x)
(if (= x target)
(conj acc (conj indices i))
acc)))
acc, (indexed coll)))]
(step [] [] coll)))
(def y [[[1 2 3] [4 5 6] [7 8 9]] [[10 11] [12 13]] [[14] [15 [16 17 4]]]])
(nested-indices-of y 4) ;=> [[0 1 0] [2 1 1 2]]
Vectors within vectors are no different to ints within vectors:
(.indexOf [[[1 2 3] [4 5 6] [7 8 9]] [[10 11] [12 13]] [[14] [15]]] [[14] [15]])
;;=> 2
The above might be a bit difficult to read, but [[14] [15]] is the third element.
Something like
(defn indexer [vec number]
(for [[x set1] (map-indexed vector vec)
[y set2] (map-indexed vector set1)
[z val] (map-indexed vector set2)
:when (= number val)]
[x y z]))
Written directly into here so not tested. Giving more context on what this would be used for might make it easier to give a good answer as this feels like something you shouldn't end up doing in Clojure.
You can also try and flatten the vectors in some way
An other solution to find the path of every occurrences of a given number.
Usually with functional programming you can go for broader, general, elegant, bite size solution. You will always be able to optimize using language constructs or techniques as you need (tail recursion, use of accumulator, use of lazy-seq, etc)
(defn indexes-of-value [v coll]
(into []
(comp (map-indexed #(if (== v %2) %1))
(remove nil?))
coll))
(defn coord' [v path coll]
(cond
;; node is a leaf: empty or coll of numbers
(or (empty? coll)
(number? (first coll)))
(when-let [indexes (seq (indexes-of-value v coll))]
(map #(conj path %) indexes))
;; node is branch: a coll of colls
(coll? (first coll))
(seq (sequence (comp (map-indexed vector)
(mapcat #(coord' v (conj path (first %)) (second %))))
coll))))
(defn coords [v coll] (coord' v [] coll))
Execution examples:
(def coll [[2 1] [] [7 8 9] [[] [1 2 2 3 2]]])
(coords 2 coll)
=> ([0 0] [3 1 1] [3 1 2] [3 1 4])
As a bonus you can write a function to test if paths are all valid:
(defn valid-coords? [v coll coords]
(->> coords
(map #(get-in coll %))
(remove #(== v %))
empty?))
and try the solution with input generated with clojure.spec:
(s/def ::leaf-vec (s/coll-of nat-int? :kind vector?))
(s/def ::branch-vec (s/or :branch (s/coll-of ::branch-vec :kind vector?
:min-count 1)
:leaf ::leaf-vec))
(let [v 1
coll (first (gen/sample (s/gen ::branch-vec) 1))
res (coords v coll)]
(println "generated coll: " coll)
(if-not (valid-coords? v coll res)
(println "Error:" res)
:ok))
Here is a function that can recursively search for a target value, keeping track of the indexes as it goes:
(ns tst.clj.core
(:use clj.core tupelo.test)
(:require [tupelo.core :as t] ))
(t/refer-tupelo)
(defn index-impl
[idxs data tgt]
(apply glue
(for [[idx val] (zip (range (count data)) data)]
(let [idxs-curr (append idxs idx)]
(if (sequential? val)
(index-impl idxs-curr val tgt)
(if (= val tgt)
[{:idxs idxs-curr :val val}]
[nil]))))))
(defn index [data tgt]
(keep-if not-nil? (index-impl [] data tgt)))
(dotest
(let [data-1 [1 2 3]
data-2 [[1 2 3]
[10 11]
[]]
data-3 [[[1 2 3]
[4 5 6]
[7 8 9]]
[[10 11]
[12 13]]
[[20]
[21]]
[[30]]
[[]]]
]
(spyx (index data-1 2))
(spyx (index data-2 10))
(spyx (index data-3 13))
(spyx (index data-3 21))
(spyx (index data-3 99))
))
with results:
(index data-1 2) => [{:idxs [1], :val 2}]
(index data-2 10) => [{:idxs [1 0], :val 10}]
(index data-3 13) => [{:idxs [1 1 1], :val 13}]
(index data-3 21) => [{:idxs [2 1 0], :val 21}]
(index data-3 99) => []
If we add repeated values we get the following:
data-4 [[[1 2 3]
[4 5 6]
[7 8 9]]
[[10 11]
[12 2]]
[[20]
[21]]
[[30]]
[[2]]]
(index data-4 2) => [{:idxs [0 0 1], :val 2}
{:idxs [1 1 1], :val 2}
{:idxs [4 0 0], :val 2}]

Implement reversing a sequence in a way that is idiomatic for Clojure

The task is to write a function that reverses a sequence. I am trying to do this as efficiently and clojure-ish as possible, but I can't figure out the best way. It doesn't feel right to create a variable and start appending items to that variable. What would seem idiomatic to me was if there was a way to do the following:
From the Clojure docs
(map-indexed (fn [idx itm] [idx itm]) "foobar")
--> ([0 \f] [1 \o] [2 \o] [3 \b] [4 \a] [5 \r])
What I have done is this:
(defn testing [x]
(map-indexed (fn [idx itm] [(- (count x) idx) itm]) x))
Which yields:
(testing "foobar")
--> ([6 \f] [5 \o] [4 \o] [3 \b] [2 \a] [1 \r])
What I can't figure out is how to essentially pack that back up into a new list with the correct indexes. Honestly, even the concept of declaring a variable is escaping me in Clojure at the moment. All my experience is with Python and Ruby.
Strings are sequable so we can use clojure.core/reduce
(defn rev [x]
(reduce #(str %2 %1) x))
(rev "foobar") ;; "raboof"
The idea is that for each iteration, we create a new string that is the next character in the sequence prepended to the current string.
All my experience is with Python and Ruby
Here is the same in Ruby:
def rev(x)
x.chars.reduce { |s, c| c + s }
end
For non-strings
(defn rev [x]
(reduce conj () x))
(rev [1 2 3 4 5]) ;; (5 4 3 2 1)
You can reverse a sequence using indexes, as your question proposes, as follows:
(defn reverse [s]
(let [v (vec s)] (map v (range (dec (count v)) -1 -1))))
... or, perversely,
(def reverse (comp rseq vec))