I'm relatively new to Clojure and have been working through 4Clojure. I'm working on problem 31, Pack a Sequence, http://www.4clojure.com/problem/31#prob-title. The goal is to pack consecutive duplicates into sub-lists. I know this can be achieved using (partition-by ...) but I wanted to attempt writing it out in full using (loop ... (recur ...)). My attempt is as follows:
(defn my-subpack
[paramSeq]
;Track the output sequence
;Track the current sub sequence
;Track the next input value to consider
(loop [outSeq []
currSub (vector (first paramSeq))
inSeq (rest paramSeq)]
;If the inSeq is empty then we return outSeq
(println (empty? inSeq))
(if (empty? inSeq)
outSeq)
;If the next value from inSeq is in the currSub then we add it to it
(if (= (first currSub) (first inSeq))
(recur outSeq (conj currSub (first inSeq)) (rest inSeq))
(recur (conj outSeq currSub) (vector (first inSeq)) (rest inSeq)))
))
The program seems to get stuck in an infinite loop so I ran it through my IDE's debugger. It appears that when inSeq is empty the if statement to return outSeq does not get run. It is skipped over as if (empty? inSeq) returns false. Below I've included a screenshot of the IDE when the infinite loop begins. Notice that (count inSeq) returns 0 in the expression evaluator but the debugger shows it as having a count of 1. Here is the IDE when Infinite Loop begins. Count shows 0 in expression evaluator but it also shows 1 in debugger.
It feels likely to me I've made some huge oversight but I just amn't experienced enough to see it yet!
The issue is that when inSeq is empty, the machine just does nothing and proceeds to the next statement (which does recur in both cases). What you probably want is to return outSeq as the result of the loop expression if inSeq is empty:
(if (empty? inSeq)
outSeq
;; If the next value from inSeq is in the currSub then we add it to it
(if (= (first currSub) (first inSeq))
(recur outSeq (conj currSub (first inSeq)) (rest inSeq))
(recur (conj outSeq currSub) (vector (first inSeq)) (rest inSeq))))
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)
I have some code which I refactored only to find out something was broken with loop. After some debugging I found out loop and with-redefs do not play well together. I realize it may not make sense to use with-redefs inside a loop, but I didn't expect it to not work. I'm not sure if its intentional or not.
This is an MCVE I've created to demonstrate the "problem":
(loop [test 3]
(with-redefs []
(if (zero? test)
"done"
(recur (dec test)))))
This gives me:
Mismatched argument count to recur, expected: 0 args, got: 1
Removing the with-redefs works as expected:
(loop [test 3]
(if (zero? test)
"done"
(recur (dec test))))
and returns "done".
What is the reason the first piece of code does not work? Is this intentional?
The explanation is in the macroexpansion of with-redefs:
(macroexpand-1
'(with-redefs []
(if (zero? test)
"done"
(recur (dec test)))))
returns:
(with-redefs-fn {}
(fn []
(if (zero? test)
"done"
(recur (dec test)))))
where you can see that because a new fn has been introduced, the recur is going to refer to that fn rather than the farther-away loop (which explains the arity exception).
There are a variety of other macros that are "incompatible" with loop in this way, because the recur needs to be in the tail position with respect to loop, and if the recur occurs inside a macro call, the macro may be manipulating the code such that the recur is no longer in tail position.
For with-redefs in particular (and a variety of other situations), a workaround could be:
(loop [test 3]
(let [[recur? val]
(with-redefs []
(if (zero? test)
[false "done"]
[true (dec test)]))]
(if recur?
(recur val)
val)))
What are other more clojure-idiomatic ways than looping over a sequence to go through a sequence, and pick up each element from it? This is the loop-version of what i mean:
(def a-seq (list 700 600 500 400 300 200 100))
(loop [s a-seq]
(if (seq s)
(do (instrument (first s)) (recur (rest s)))
"End"))
I will be feeding the (first s) as a frequency into a sin-wave generator as follows (in the overtone lib):
(use 'overtone.core)
(definst instrument [frequency 0] (sin-osc frequency))
The map function is what you want to use.
(map instrument a-seq)
This will call the instrument function once for each element in a-seq, in order.
Note that map is lazy, so you will need to consume the results of map to guarantee that any side-effects to take place, or call doall.
Use doseq:
(doseq [item a-seq]
(println item))
(println "End")
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.
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)))))))