Explanation for combination of cycle drop and take in clojure - clojure

I am trying to understand the implementation of rotating a sequence to which the answer i find in git hub is below
(fn [n coll]
(take (count coll) (drop (mod n (count coll)) (cycle coll))))
Could you please explain what is exacty happening here
(take 6 (drop 1 (cycle ["a" "b" "c"])))
("b" "c" "a" "b" "c" "a")
How is this being produced

From the documentation of cycle:
Returns a lazy (infinite!) sequence of repetitions of the items in coll.
So in your example:
(cycle ["a" "b" "c"])
;; => ["a" "b" "c" "a" "b" "c" "a" "b" "c" "a" "b" "c" ...]
(toward infinity and beyond)
To cut down an infinite sequence, you have to use take which takes the first n element of a sequence. So:
(take 6 (cycle ["a" "b" "c"]))
;; => ["a" "b" "c" "a" "b" "c"]
In your example, just before calling take, you use drop which left out the first n element of a sequence. So:
(drop 1 (cycle ["a" "b" "c"]))
;; => ["b" "c" "a" "b" "c" "a" "b" "c" "a" "b" "c" ...]
(take 6 (drop 1 (cycle ["a" "b" "c"])))
;; => ["b" "c" "a" "b" "c" "a"]
You can learn more about lazy sequences from this chapter of "Clojure from the Brave and True".

Related

clojure: Next element of an item that can fallback to first

I'd like to create a getnext fn that looks for a element in a coll and when match, return the next element. Also, it should return the first element if the last one is passed as argument.
(def coll ["a" "b" "c" "d"])
(defn get-next [coll item] ...)
(get-next coll "a") ;;=> "b"
(get-next coll "b") ;;=> "c"
(get-next coll "c") ;;=> "d"
(get-next coll "d") ;;=> "a" ; back to the beginning
Thanks!
How about this:
Append first item at the end of the sequence (lazily),
Drop non-items,
Return what's left (nil if item not found).
Or in code:
(defn get-next [coll item]
(->> (concat coll [(first coll)])
(drop-while (partial not= item))
second))
There are certainly purer lisp approaches than this one but, hey, as long as we got .indexOf, we might as well use it. The key to simplicity is that, plus cycle, so we don't have to check for the last item.
(defn get-next [coll item]
(nth (cycle coll) (inc (.indexOf coll item))))
Some test runs:
(get-next ["A" "B" "C" "D"] "B")
=> "C"
(get-next ["A" "B" "C" "D"] "D")
=> "A"
(get-next ["A" "B" "C" "D"] "E")
=> "A"
Whoops! Well, we didn't specify what we wanted to do if the item wasn't in the collection. Idiomatically, we would return nil, so we need a new get-next:
(defn get-next-2 [coll item]
(let [i (.indexOf coll item)]
(if (= -1 i) nil (nth (cycle coll) (inc i)))))
And now we catch the not-there case:
(get-next-2 ["A" "B" "C" "D"] "Q")
=> nil
I would convert coll to map and use it for lookups:
(def doll (zipmap coll (rest (cycle coll))))
(doll "a") => "b"
(doll "b") => "c"
(doll "d") => "a"
This is a good job for drop-while:
(defn get-next
[coll item]
(let [remainder (drop-while #(not= % item) coll)]
(when (empty? remainder)
(throw (IllegalArgumentException. (str "Item not found: " item))))
(if (< 1 (count remainder))
(nth remainder 1)
(first coll))))
(dotest
(let [coll [1 2 3 4]]
(is= 2 (get-next coll 1))
(is= 3 (get-next coll 2))
(is= 4 (get-next coll 3))
(is= 1 (get-next coll 4))
(throws? (get-next coll 5))))

Clojure partition list of strings, accumulating the result

I'm sorry about the lack of precision in the title, but it might illustrate my lack of clojure experience.
I'm trying to take a large list of strings, and convert that list into another list of strings, concatenating as I go until the accumulator is less than some length.
For example, if I have
[ "a" "bc" "def" "ghij" ]
and my max string length is 4, I would walk down the list, accumulating the concat, until my accumulation len > 4, and then start the accumulator from scratch. My result would look like:
[ "abc" "def" "ghij" ]
I can't seem to come up with the proper incantation for partition-by, and it's driving me a little crazy. I've been trying to make my accumulator an atom (but can't seem to figure out where to reset!), but other than that, I can't see where/how to keep track of my accumulated string.
Thanks in advance to anyone taking mercy on me.
(defn catsize [limit strs]
(reduce (fn [res s]
(let [base (peek res)]
(if (> (+ (.length ^String base) (.length ^String s)) limit)
(conj res s)
(conj (pop res) (str base s)))))
(if (seq strs) [(first strs)] [])
(rest strs)))
Here's my take on this:
(defn collapse [maxlen xs]
(let [concats (take-while #(< (count %) maxlen) (reductions str xs))]
(cons (last concats) (drop (count concats) xs))))
(collapse 4 ["a" "bc" "def" "ghij"])
;; => ("abc" "def" "ghij")
This gets pretty close. I'm not sure why you have j at the end of the final string.
(sequence
(comp
(mapcat seq)
(partition-all 3)
(map clojure.string/join))
["a" "bc" "def" "ghij"]) => ("abc" "def" "ghi" "j")
Here is how I would do it:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(def bound 4)
(defn catter [strings-in]
(loop [merged-strs []
curr-merge (first strings-in)
remaining-strs (rest strings-in)]
;(newline) (spyx [merged-strs curr-merge remaining-strs])
(if (empty? remaining-strs)
(conj merged-strs curr-merge)
(let ; try using 'let-spy' instead
[new-str (first remaining-strs)
new-merge (str curr-merge new-str)]
(if (< (count new-merge) bound)
(recur merged-strs new-merge (rest remaining-strs))
(recur (conj merged-strs curr-merge) new-str (rest remaining-strs)))))))
(dotest
(is= ["abc" "def" "ghij"] (catter ["a" "bc" "def" "ghij"]) )
(is= ["abc" "def" "ghij"] (catter ["a" "b" "c" "def" "ghij"]) )
(is= ["abc" "def" "ghij"] (catter ["a" "b" "c" "d" "ef" "ghij"]) )
(is= ["abc" "def" "ghij"] (catter ["a" "bc" "d" "ef" "ghij"]) )
(is= ["abc" "def" "ghij"] (catter ["a" "bc" "d" "e" "f" "ghij"]) )
(is= ["abc" "def" "gh" "ij"] (catter ["abc" "d" "e" "f" "gh" "ij"]) )
(is= ["abc" "def" "ghi" "j"] (catter ["abc" "d" "e" "f" "ghi" "j"]) )
(is= ["abcdef" "ghi" "j"] (catter ["abcdef" "ghi" "j"]) )
(is= ["abcdef" "ghi" "j"] (catter ["abcdef" "g" "h" "i" "j"]) )
)
You will need to add [tupelo "0.9.71"] to project dependencies.
Update:
If you user spy and let-spy, you can see the process the algorithm uses to arrive at the result. For example:
(catter ["a" "b" "c" "d" "ef" "ghij"]) ) => ["abc" "def" "ghij"]
-----------------------------------------------------------------------------
strings-in => ["a" "b" "c" "d" "ef" "ghij"]
[merged-strs curr-merge remaining-strs] => [[] "a" ("b" "c" "d" "ef" "ghij")]
new-str => "b"
new-merge => "ab"
[merged-strs curr-merge remaining-strs] => [[] "ab" ("c" "d" "ef" "ghij")]
new-str => "c"
new-merge => "abc"
[merged-strs curr-merge remaining-strs] => [[] "abc" ("d" "ef" "ghij")]
new-str => "d"
new-merge => "abcd"
[merged-strs curr-merge remaining-strs] => [["abc"] "d" ("ef" "ghij")]
new-str => "ef"
new-merge => "def"
[merged-strs curr-merge remaining-strs] => [["abc"] "def" ("ghij")]
new-str => "ghij"
new-merge => "defghij"
[merged-strs curr-merge remaining-strs] => [["abc" "def"] "ghij" ()]
Ran 2 tests containing 10 assertions.
0 failures, 0 errors.

Clojure Convert PersistentVector of strings to PersistentVector of keywords

Given the following vector: ["a" "b" "c"] how can I convert it to [:a :b :c]
You can use mapv:
(mapv keyword ["a" "b" "c"])
(vec (map keyword ["a" "b" "c"]))

map-indexed function on arbitrary number of collections

There is a map-indexed function in Clojure, however (as of version "1.8.0"), it only accepts up to two arguments (source):
As I could not see any reason not to have an arbitrary number of arguments, I am trying to write my own version (reusing the existing map-indexed function) :
(defn map-index
"like clojure.core/map-indexed but accepts more than two arguments"
([f] ;;(partial (map f (range))) ;; TODO transducer ?
)
([f coll] (map f (range) coll))
([f c1 c2] (map f (range) c1 c2))
([f c1 c2 c3] (map f (range) c1 c2 c3))
([f c1 c2 c3 & colls]
;; Warning !
;; calling map-indexed with a final parameter freezes my REPL
(map f (conj colls c3 c2 c1 (range)))))
((map-indexed list) ["a" "b" "c"])
(map-indexed list ["a" "b" "c"])
((map-index list) ["a" "b" "c"]) ;; KO
(map-index list ["a" "b" "c"])
(map-index list ["a" "b" "c"] ["d" "e" "f"]) ;; OK
(map-index list ["a" "b" "c"] ["d" "e" "f"] ["g" "h" "i"]) ;; OK
(map-index list ["a" "b" "c"] ["d" "e" "f"] ["g" "h" "i"] ["k" "l" "m"]) ;; freezes the REPL
How should I write this map-index function ?
I would just write it like this:
(defn map-index
([f]
(map-indexed f))
([f & colls]
(apply map f (range) colls)))
Unless you really care about performance, there's no need to overcomplicate things with extra arities.
It's worth noting that since the transducer version here simply calls map-indexed, it won't work for an arbitrary number of collections. I'll leave it up to you to implement that if you need to do so.

Simple comparator does not sort as (I) expected

I expected this code snippet to produce the original vector, but sorted in a case-insensitive way. Instead I get the original vector untouched. Why doesn't my comparator work?
user=> (ns user (require [clojure.contrib.string :as str]))
nil
user=> (sort
(comparator #(compare (str/upper-case %1) (str/upper-case %2)))
["B" "a" "c" "F" "r" "E"])
("B" "a" "c" "F" "r" "E")
comparator returns a java.util.Comparator when given a predicate (a function which returns true or false). You don't need it if you're using compare explicitly. So just:
(sort #(compare (str/upper-case %1) (str/upper-case %2))
["B" "a" "c" "F" "r" "E"])
;=> ("a" "B" "c" "E" "F" "r")
Alternatively, use sort-by:
(sort-by str/upper-case ["B" "a" "c" "F" "r" "E"])
;=> ("a" "B" "c" "E" "F" "r")
compare is not a predicate, it's a comparator.