Nested list traversal - clojure

I would like to write a function that given a possibly nested list would return me a list of the indexes of each leaf, such that I can reduce the list with nth and those indexes and obtain each leaf. Ex: Given lst = ((a b) c) it would return ((0 0) (0 1) (1)) because (reduce nth lst [0 0]) = a (reduce nth lst [0 1]) = b and (reduce nth lst [1]) = c.
EDIT: Here is my solution using clojure.zip. Anyone can come up with a more elegant one?
(defn tree-indexes [zipper matcher pos accumulator]
(loop [loc zipper pos pos accumulator accumulator]
(if (z/end? loc)
accumulator
(if (matcher (z/node loc))
(if (z/right loc)
(recur (z/next loc) (update-in pos [(- (count pos) 1)] inc) (conj accumulator [(z/node loc) pos]))
(if (z/end? (z/next loc))
(recur (z/next loc) pos (conj accumulator [(z/node loc) pos]))
(recur (z/next loc) (update-in (vec (butlast pos)) [(- (count (vec (butlast pos))) 1)] inc) (conj accumulator [(z/node loc) pos]))))
(recur (z/next loc) (conj pos 0) accumulator)))))

I have a (non-tail-)recursive solution but there probably is some kind of iterative approach:
(defn list-indices
([l] (list-indices l []))
([l parent-indices]
(->> l
(map-indexed
(fn [i element]
(let [indices (conj parent-indices i)]
(if (sequential? element)
(list-indices element indices)
[indices]))))
(apply concat))))

Related

How to rewrite this function to be tail-recursive?

I'm reading the book The Little Schemer, I implement the functions in Clojure, How to rewrite this function as a tail-recursive function in Clojure?
(defn rember*
[a lat]
(cond
(empty? lat) []
(clojure.inspector/atom? (first lat))
(if (= a (first lat))
(rember* a (rest lat))
(cons (first lat)
(rember* a (rest lat))))
:else
(cons (rember* a (first lat))
(rember* a (rest lat)))))
Here is a translation of your code to a version that uses a loop and two lists instead of recursion in order to maintain program state:
(defn rember* [a lat]
(loop [[[op-type a lat] & rest-ops] (list [:rember a lat])
data '()]
(case op-type
nil (first data)
:value (recur rest-ops (cons a data))
:rember (cond
(empty? lat) (recur rest-ops (cons '() data))
(clojure.inspector/atom? (first lat))
(if (= a (first lat))
(recur (cons [:rember a (rest lat)] rest-ops) data)
(recur (into rest-ops [[:cons]
[:rember a (rest lat)]
[:value (first lat)]])
data))
:else (recur (into rest-ops [[:cons]
[:rember a (rest lat)]
[:rember a (first lat)]])
data))
:cons (let [[a b & data] data]
(recur rest-ops (cons (cons b a) data))))))
I agree, loop-recur is the more clojuresk answer!
But for a tail-recursive version, you could also do:
(def atom? (complement coll?))
(defn rember*
([a lat] (rember* a lat []))
([a lat acc]
(cond (empty? lat) (seq acc) ;; or just `acc` when result should be vector
(atom? (first lat)) (if (= a (first lat))
(rember* a (rest lat) acc)
(rember* a (rest lat) (conj acc (first lat))))
:else (rember* a (rest lat) (conj acc (rember* a (first lat)))))))
With this, you can easily build loop-recur by replacing the recursive function call using recur. However note that the last call of rember* cannot be placed by recur - because you cannot nest recur calls.
(defn rember* [a lat]
(loop [a a
lat lat
acc []]
(cond (empty? lat) (seq acc)
(atom? (first lat)) (if (= a (first lat))
(recur a (rest lat) acc)
(recur a (rest lat) (conj acc (first lat))))
:else (recur a (rest lat) (conj acc (rember* a (first lat)))))))

How can I improve my solution to the Hackerrank Maximum Element challenge to avoid timeouts?

I am trying to complete the Hackerrank Maximum Element challenge found here: https://www.hackerrank.com/challenges/maximum-element/problem
My solution produces the correct output, but times out on the final test cases beginning with #17.
Initially, I used a list and loop/recur to get my answer:
(defn get-query []
(map #(Integer/parseInt %) (clojure.string/split (read-line) #" ")))
(defn stack-stepper [query stack]
(condp = (first query)
1 (conj stack (second query))
2 (rest stack)
3 (do (println (apply max stack)) stack)))
(loop [stack '()
queries-left (Integer/parseInt (read-line))]
(if (> queries-left 0)
(recur (stack-stepper (get-query) stack) (dec queries-left))))
After some research and feedback from other channels, I tried a vector instead of a list, and reduce instead of loop/recur, but the results were the same.
(defn get-query []
(map #(Integer/parseInt %) (clojure.string/split (read-line) #" ")))
(defn get-queries []
(loop [queries []
queries-left (Integer/parseInt (read-line))]
(if (= queries-left 0)
queries
(recur (conj queries (get-query)) (dec queries-left)))))
(defn stack-stepper [stack query]
(condp = (first query)
1 (conj stack (second query))
2 (pop stack)
3 (do (println (apply max stack)) stack)))
(reduce stack-stepper [] (get-queries))
I am still new to FP and Clojure and I would really like to understand what I am missing. I greatly appreciate your time and help!
HackerRank problems are often very demanding from a performance point of view.
The obvious thing to try first is using a transient vector so see if that helps. I tried this:
(let [in (clojure.string/split (slurp *in*) #"\s")
tests (first in)
input-data (map #(Integer/parseInt %) (rest in))]
(loop [v (transient [])
d input-data]
(when (seq d)
(condp = (first d)
1 (recur (conj! v (second d)) (drop 2 d))
2 (recur (pop! v) (rest d))
3 (let [pv (persistent! v)] (println (apply max pv)) (recur (transient pv) (rest d)))))))
If failed at the same point as your solution. Clearly they're looking for something cleverer than that.
The obvious bottleneck is the calculation of the max value on the current stack, which gets re-calculated each time. We can instead save the previous max value on the stack, and recover it as the current max value when we pop the stack:
(defn peek! [tvec] (get tvec (dec (count tvec))))
(let [in (clojure.string/split (slurp *in*) #"\s")
tests (first in)
input-data (map #(Integer/parseInt %) (rest in))]
(loop [v (transient [])
m 0
d input-data]
(when (seq d)
(condp = (first d)
1 (let [snd (second d)
max-now (max m snd)]
(recur (conj! v {:val snd :max-prev m}) max-now (drop 2 d)))
2 (let [popped (peek! v)
max (if popped (:max-prev popped) 0)]
(recur (pop! v) max (rest d)))
3 (do
(println m)
(recur v m (rest d)))))))
Which puts me at rank 1 on the leaderboard :)

clojure performance on badly performing code

I have completed this problem on hackerrank and my solution passes most test cases but it is not fast enough for 4 out of the 11 test cases.
My solution looks like this:
(ns scratch.core
(require [clojure.string :as str :only (split-lines join split)]))
(defn ascii [char]
(int (.charAt (str char) 0)))
(defn process [text]
(let [parts (split-at (int (Math/floor (/ (count text) 2))) text)
left (first parts)
right (if (> (count (last parts)) (count (first parts)))
(rest (last parts))
(last parts))]
(reduce (fn [acc i]
(let [a (ascii (nth left i))
b (ascii (nth (reverse right) i))]
(if (> a b)
(+ acc (- a b))
(+ acc (- b a))))
) 0 (range (count left)))))
(defn print-result [[x & xs]]
(prn x)
(if (seq xs)
(recur xs)))
(let [input (slurp "/Users/paulcowan/Downloads/input10.txt")
inputs (str/split-lines input)
length (read-string (first inputs))
texts (rest inputs)]
(time (print-result (map process texts))))
Can anyone give me any advice about what I should look at to make this faster?
Would using recursion instead of reduce be faster or maybe this line is expensive:
right (if (> (count (last parts)) (count (first parts)))
(rest (last parts))
(last parts))
Because I am getting a count twice.
You are redundantly calling reverse on every iteration of the reduce:
user=> (let [c [1 2 3]
noisey-reverse #(doto (reverse %) println)]
(reduce (fn [acc e] (conj acc (noisey-reverse c) e))
[]
[:a :b :c]))
(3 2 1)
(3 2 1)
(3 2 1)
[(3 2 1) :a (3 2 1) :b (3 2 1) :c]
The reversed value could be calculated inside the containing let, and would then only need to be calculated once.
Also, due to the way your parts is defined, you are doing linear time lookups with each call to nth. It would be better to put parts in a vector and do indexed lookup. In fact you wouldn't need a reversed parts, and could do arithmetic based on the count of the vector to find the item to look up.

Append to a vector in a function

I have two columns (vectors) of different length and want to create a new vector of rows (if the column has enough elements). I'm trying to create a new vector (see failed attempt below). In Java this would involve the steps: iterate vector, check condition, append to vector, return vector. Do I need recursion here? I'm sure this is not difficult to solve, but it's very different than procedural code.
(defn rowmaker [colA colB]
"create a row of two columns of possibly different length"
(let [mia (map-indexed vector colA)
rows []]
(doseq [[i elA] mia]
;append if col has enough elements
(if (< i (count colA)) (vec (concat rows elA))) ; ! can't append to rows
(if (< i (count colB)) (vec (concat rows (nth colB i)))
;return rows
rows)))
Expected example input/output
(rowMaker ["A1"] ["B1" "B2"])
; => [["A1" "B1“] [“" "B2"]]
(defn rowMaker [colA colB]
"create a row from two columns"
(let [ca (count colA) cb (count colB)
c (max ca cb)
colA (concat colA (repeat (- c ca) ""))
colB (concat colB (repeat (- c cb) ""))]
(map vector colA colB)))
(defn rowmaker
[cols]
(->> cols
(map #(concat % (repeat "")))
(apply map vector)
(take (->> cols
(map count)
(apply max)))))
I prefer recursion to counting the number of items in collections. Here is my solution.
(defn row-maker
[col-a col-b]
(loop [acc []
as (seq col-a)
bs (seq col-b)]
(if (or as bs)
(recur (conj acc [(or (first as) "") (or (first bs) "")])
(next as)
(next bs))
acc)))
The following does the trick with the given example:
(defn rowMaker [v1 v2]
(mapv vector (concat v1 (repeat "")) v2))
(rowMaker ["A1"] ["B1" "B2"])
;[["A1" "B1"] ["" "B2"]]
However, it doesn't work the other way round:
(rowMaker ["B1" "B2"] ["A1"])
;[["B1" "A1"]]
To make it work both ways, we are going to have to write a version of mapv that fills in for sterile sequences so long as any sequence is fertile. Here is a corresponding lazy version for map, which will work for infinite sequences too:
(defn map-filler [filler f & colls]
(let [filler (vec filler)
colls (vec colls)
live-coll-map (->> colls
(map-indexed vector)
(filter (comp seq second))
(into {}))
split (fn [lcm] (reduce
(fn [[x xm] [i coll]]
(let [[c & cs] coll]
[(assoc x i c) (if cs (assoc xm i cs) xm)]))
[filler {}]
lcm))]
((fn expostulate [lcm]
(lazy-seq
(when (seq lcm)
(let [[this thoses] (split lcm)]
(cons (apply f this) (expostulate thoses))))))
live-coll-map)))
The idea is that you supply a filler sequence with one entry for each of the collections that follow. So we can now define your required rowmaker function thus:
(defn rowmaker [& colls]
(apply map-filler (repeat (count colls) "") vector colls))
This will take any number of collections, and will fill in blank strings for exhausted collections.
(rowmaker ["A1"] ["B1" "B2"])
;(["A1" "B1"] ["" "B2"])
(rowmaker ["B1" "B2"] ["A1"])
;(["B1" "A1"] ["B2" ""])
It works!
(defn make-row
[cola colb r]
(let [pad ""]
(cond
(and (not (empty? cola))
(not (empty? colb))) (recur (rest cola)
(rest colb)
(conj r [(first cola) (first colb)]))
(and (not (empty? cola))
(empty? colb)) (recur (rest cola)
(rest colb)
(conj r [(first cola) pad]))
(and (empty? cola)
(not (empty? colb))) (recur (rest cola)
(rest colb)
(conj r [pad (first colb)]))
:else r)))

insertion sort using clojure

I ran into this error when I try to evaluate the last line below: "No message. [Thrown class java.lang.NullPointerException]"
(defn my-insertion-sort [lst]
(loop [list lst result '()]
(if-not (seq? list) result
(recur (rest list) (my-insert (first list) result)))))
(defn my-insert [n lst]
(cond (nil? lst) (list n)
(> (first lst) n) (conj lst n)
:else
(conj (my-insert n (rest lst)) (first lst))))
(my-insertion-sort '(2 1 3))
Where goes wrong with "my-insertion-sort" function?
(defn my-insertion-sort [lst]
(loop [list lst result '()]
(if (empty? list) result
(recur (rest list) (my-insert (first list) result)))))
(defn my-insert [n lst]
(cond
(empty? lst) (list n)
(> (first lst) n) (conj lst n)
:else (conj (my-insert n (rest lst)) (first lst))))