I encountered the StackOverflowError for the following code:
(defn recursive-reverse
([coll] (recursive-reverse [coll nil]))
([coll acc]
(if (= coll '()) acc
(recur (rest coll) (cons (first coll) acc)))))
though using loop would make it work:
(defn recursive-reverse [lst]
(loop [coll lst acc nil]
(if (= coll '()) acc
(recur (rest coll) (cons (first coll) acc)))))
What goes wrong with the prior code without loop?
Your bug is here:
([coll] (recursive-reverse [coll nil]))
You're calling recursive-reverse with one argument (a vector). This calls the same argument list of the function, so it does it recursively and creates a stack frame every time.
Change it to:
([coll] (recursive-reverse coll nil))
and you should be right.
(Also, separate issue, but I would generally do checking for nil rather than '() and using next rather than rest. I don't think it has any real advantage in terms of performance or anything, but it seems cleaner to me.)
This worked for me:
(defn recursive-reverse
([coll] (recursive-reverse coll nil))
([coll acc]
(if (= coll '()) acc
(recur (rest coll) (cons (first coll) acc)))))
You passed the arguments to recursive-reverse inside a couple of unnecessary brackets, that's all.
Related
At the end of CLOJURE for the BRAVE and TRUE's Chapter 4 there's an exercise: make an append function that appends a new entry to a list.
What's the most efficient way to do so?
From what I understand of datatypes in general, if conj prepends elements to a list, that simply means that consistently appending to a list is either silly or the choice of using a list type was silly.
Anyway, the solution I've written is this
(defn append
[lst item]
(into '() (conj (into '() lst) item)))
Well, that's actually the same as
(defn append
[lst item]
(reverse (conj (reverse lst) item)))
I believe, so probably is costly because I reverse the list twice?
Another solution I could think of is
(defn append
[lst item]
(apply list (conj (apply vector lst) item)))
But they all seem to traverse the sequence of values twice, so I don't see why any one shoiuld be better than another.
Is there the proper way to accomplish the task?
to avoid the double traversal, you could use the classic recursive approach. Something like this:
(defn append [lst item]
(loop [res [] lst lst]
(if (seq lst)
(recur (conj res (first lst))
(rest lst))
(seq (conj res item)))))
so, it just rebuilds the list from scratch.
To make it's performance better in clojure, you can optimize it with transient collection:
(defn append' [lst item]
(loop [res (transient []) lst lst]
(if (seq lst)
(recur (conj! res (first lst))
(rest lst))
(seq (persistent! (conj! res item))))))
but yes, as another answer proposes, you should carefully pick the proper data structure for every specific task, so in practice you would want to use vector.
As of concat variant, it comes for free (since it is lazy), but it has this known pitfall, which is stack overflow on applying a lot of stacked lazy functions:
user> (defn append-concat [lst item]
(concat lst [item]))
user> (reduce append-concat () (range 1000000))
;; Error printing return value (StackOverflowError) at clojure.lang.LazySeq/sval (LazySeq.java:42).
There is a reason, why conj adds a new item at the start of the list and not at its end. Because you have to traverse the list to add something at its end. This is not very efficient. And it is because of the nature of a linked list. That's why in lisp you cons new items onto a list and at the very end reverse it. This way, you traverse the list just once while building it.
In Clojure, if you want to add to a list at the end, it is more idiomatic not to use a list but instead the vector type. On a vector, conj adds right to the end.
But let's say, you want to traverse a list once and add to the end.
That is actually:
(defn my-append [lst item] (concat lst (list item)))
(my-append '(1 2 3) 4)
;; (1 2 3 4)
but as I said, if you want to add repeatedly at the end, don't use a list, but a vector and conj to the end.
;; more idiomatic clojure in thisst to add something at its end. This is not very efficient. And it is because of the nature of a linked list. Tha case
(def v [1 2 3])
(conj v 4) ;; [1 2 3 4] ;; unbeatable in efficience, because no traversal!
;; and convert to a list
;; e.g.
(def l (conj v 4))
(seq l) ;; (1 2 3 4)
How would I create a transducer from the following ordinary code, where combo is the alias for clojure.math.combinatorics:
(defn row->evenly-divided [xs]
(->> (combo/combinations (sort-by - xs) 2)
(some (fn [[big small]]
(assert (>= big small))
(let [res (/ big small)]
(when (int? res)
res))))))
As noted in a comment transducers are only applicable for processing each item. With this is mind I've made the code a little more transducer friendly by shifting the sorting so that it is now being done for each item. I don't think there's anything that can be done about the combinations part however!
(defn row->evenly-divided [xs]
(->> (combo/combinations xs 2)
(some (fn [xy]
(let [res (apply / (sort-by - xy))]
(when (int? res)
res))))))
This is the same function but with an introduced transducer:
(def x-row->evenly-divided (comp
(map (partial sort-by -))
(map (partial apply /))
(filter int?)))
(defn row->evenly-divided-2 [xs]
(->> (combo/combinations xs 2)
(sequence x-row->evenly-divided)
first))
I have this Lisp code, and I'm trying to convert it into Clojure code.
(defun copy-tree (tr)
(if (atom tr)
tr
(cons (copy-tree (car tr))
(copy-tree (crd tr)))))
It seems like that Clojure doesn't have Lisp's atom (or atom in Clojure has very different meaning), I had to modify the code as follows. (Am I using atom? wrong or there is something else....?)
(defn single-valued?
[x]
(not (or (nil? x)
(.. x getClass isArray)
(some #(instance? % x) [clojure.lang.Counted
clojure.lang.IPersistentCollection
java.util.Collection
java.util.Map]))))
(defn copy-tree [tr]
(if (or (= tr ()) (single-valued? tr))
tr
(cons (copy-tree (first tr))
(copy-tree (rest tr)))))
The code works fine, but is there better way to replace Lisp's atom function?
I think you'll find this behaves apropriately:
(def single-valued? (complement coll?))
the difference is that it will bottom out sooner for nil -- (rest nil) is () which finally does not recur, but ((complement coll?) nil) returns true, so stops the recursion one step sooner.
I had an idea for a higher-order function today that I'm not sure how to write. I have several sparse, lazy infinite sequences, and I want to create an abstraction that lets me check to see if a given number is in any of these lazy sequences. To improve performance, I wanted to push the values of the sparse sequence into a hashmap (or set), dynamically increasing the number of values in the hashmap whenever it is necessary. Automatic memoization is not the answer here due to sparsity of the lazy seqs.
Probably code is easiest to understand, so here's what I have so far. How do I change the following code so that the predicate uses a closed-over hashmap, but if needed increases the size of the hashmap and redefines itself to use the new hashmap?
(defn make-lazy-predicate [coll]
"Returns a predicate that returns true or false if a number is in
coll. Coll must be an ordered, increasing lazy seq of numbers."
(let [in-lazy-list? (fn [n coll top cache]
(if (> top n)
(not (nil? (cache n)))
(recur n (next coll) (first coll)
(conj cache (first coll)))]
(fn [n] (in-lazy-list? n coll (first coll) (sorted-set)))))
(def my-lazy-list (iterate #(+ % 100) 1))
(let [in-my-list? (make-lazy-predicate my-lazy-list)]
(doall (filter in-my-list? (range 10000))))
How do I solve this problem without reverting to an imperative style?
This is a thread-safe variant of Adam's solution.
(defn make-lazy-predicate
[coll]
(let [state (atom {:mem #{} :unknown coll})
update-state (fn [{:keys [mem unknown] :as state} item]
(let [[just-checked remainder]
(split-with #(<= % item) unknown)]
(if (seq just-checked)
(-> state
(assoc :mem (apply conj mem just-checked))
(assoc :unknown remainder))
state)))]
(fn [item]
(get-in (if (< item (first (:unknown #state)))
#state
(swap! state update-state item))
[:mem item]))))
One could also consider using refs, but than your predicate search might get rolled back by an enclosing transaction. This might or might not be what you want.
This function is based on the idea how the core memoize function works. Only numbers already consumed from the lazy list are cached in a set. It uses the built-in take-while instead of doing the search manually.
(defn make-lazy-predicate [coll]
(let [mem (atom #{})
unknown (atom coll)]
(fn [item]
(if (< item (first #unknown))
(#mem item)
(let [just-checked (take-while #(>= item %) #unknown)]
(swap! mem #(apply conj % just-checked))
(swap! unknown #(drop (count just-checked) %))
(= item (last just-checked)))))))
I have just start reading Let over lambda and I thought I would try and write a clojure version of the block-scanner in the closures chapter.
I have the following so far:
(defn block-scanner [trigger-string]
(let [curr (ref trigger-string) trig trigger-string]
(fn [data]
(doseq [c data]
(if (not (empty? #curr))
(dosync(ref-set curr
(if (= (first #curr) c)
(rest #curr)
trig)))))
(empty? #curr))))
(def sc (block-scanner "jihad"))
This works I think, but I would like know what I did right and what I could do better.
I would not use ref-set but alter because you don't reset the state to a completely new value, but update it to a new value which is obtained from the old one.
(defn block-scanner
[trigger-string]
(let [curr (ref trigger-string)
trig trigger-string]
(fn [data]
(doseq [c data]
(when (seq #curr)
(dosync
(alter curr
#(if (-> % first (= c))
(rest %)
trig)))))
(empty? #curr))))
Then it is not necessary to use refs since you don't have to coordinate changes. Here an atom is a better fit, since it can be changed without all the STM ceremony.
(defn block-scanner
[trigger-string]
(let [curr (atom trigger-string)
trig trigger-string]
(fn [data]
(doseq [c data]
(when (seq #curr)
(swap! curr
#(if (-> % first (= c))
(rest %)
trig))))
(empty? #curr))))
Next I would get rid of the imperative style.
it does more than it should: it traverses all data - even if we found a match already. We should stop early.
it is not thread-safe, since we access the atom multiple times - it might change in between. So we must touch the atom only once. (Although this is probably not interesting in this case, but it's good to make it a habit.)
it's ugly. We can do all the work functionally and just save the state, when we come to a result.
(defn block-scanner
[trigger-string]
(let [state (atom trigger-string)
advance (fn [trigger d]
(when trigger
(condp = d
(first trigger) (next trigger)
; This is maybe a bug in the book. The book code
; matches "foojihad", but not "jijihad".
(first trigger-string) (next trigger-string)
trigger-string)))
update (fn [trigger data]
(if-let [data (seq data)]
(when-let [trigger (advance trigger (first data))]
(recur trigger (rest data)))
trigger))]
(fn [data]
(nil? (swap! state update data)))))