I have the following snippet:
(defn explode [e]
(seq [e e e e]))
(defn f [coll]
(when-first [e coll]
(cons e
(lazy-seq (f (lazy-cat (next coll)
(explode e)))))))
When I try to access an element, I get a StackOverflow error:
user=> (nth (f (seq [1 2 3])) 1000)
3
user=> (nth (f (seq [1 2 3])) 10000)
StackOverflowError clojure.core/concat/fn--3923 (core.clj:678)
How can I structure this code in a way that doesn't blow the stack?
You'll have to keep track of the remaining work explicitly, perhaps like so:
(defn f [coll]
(letfn [(go [xs q]
(lazy-seq
(cond
(seq xs)
(cons (first xs)
(go (next xs) (conj q (explode (first xs)))))
(seq q)
(go (peek q) (pop q)))))]
(go coll clojure.lang.PersistentQueue/EMPTY)))
From the REPL:
(nth (f [1 2 3]) 1000)
;= 3
(nth (f [1 2 3]) 10000)
;= 2
;; f-orig is f as found in the question text
(= (take 1000 (f-orig [1 2 3])) (take 1000 (f [1 2 3])))
;= true
Related
I was trying to implement selection sort O(n2) in clojure. Yes the underlying sort uses the very efficient java's array sort. However this is a learning exercise.
The code below works, however I was wondering if there is a more idiomatic way of rewriting it, as the below seems clunky -
(defn mins [coll]
(reduce (fn[[min-coll rest-coll] val]
(case (compare val (first min-coll))
-1 [[val] (apply conj rest-coll min-coll)]
0 [(conj min-coll val) rest-coll]
1 [min-coll (conj rest-coll val)]))
[[(first coll)] []]
(rest coll)))
;; (mins [3 1 1 2]) => [[1 1] [3 2]]
(defn selection-sort [coll]
(loop [[sorted coll] [[] coll]]
(let [[s c] (mins coll)]
(if-not (seq coll)
sorted
(recur [(concat sorted s) c])))))
(selection-sort [3 1 1 2 5 7 8 8 4 6])
A functional solution could be:
(defn selection-sort
[input]
(let [ixs (vec (range (count input)))
min-key-from (fn [acc ix] (apply min-key acc (subvec ixs ix)))
swap (fn [coll i j] (assoc coll i (coll j) j (coll i)))]
(reduce
(fn [acc ix] (swap acc ix (min-key-from acc ix))) input ixs)))
You can use the following:
(defn remove-first [coll e]
(if-let [pos (and (seq coll) (.indexOf coll e))]
(vec (concat
(subvec coll 0 pos)
(subvec coll (inc pos))))
coll))
(defn best [coll f]
(reduce f
(first coll)
(rest coll)))
(defn select-sort
([coll] (select-sort coll min))
([coll fmin]
(loop [sorted (transient []) c (vec coll)]
(if (seq c)
(let [n (best c fmin)]
(recur (conj! sorted n) (remove-first c n)))
(persistent! sorted)))))
=> (select-sort [3 5 2])
=> [2 3 5]
=> (select-sort [3 5 2] max)
=> [5 3 2]
I have sequence in clojure of theem
(1 2 3 4)
how can I get all the tails of sequence like
((1 2 3 4) (2 3 4) (3 4) (4) ())
Another way to get all tails is by using the reductions function.
user=> (def x '(1 2 3 4))
#'user/x
user=> (reductions (fn [s _] (rest s)) x x)
((1 2 3 4) (2 3 4) (3 4) (4) ())
user=>
If you want to do this with higher-level functions, I think iterate would work well here:
(defn tails [xs]
(concat (take-while seq (iterate rest xs)) '(()))
However, I think in this case it would be cleaner to just write it with lazy-seq:
(defn tails [xs]
(if-not (seq xs) '(())
(cons xs (lazy-seq (tails (rest xs))))))
Here is one way.
user=> (def x [1 2 3 4])
#'user/x
user=> (map #(drop % x) (range (inc (count x))))
((1 2 3 4) (2 3 4) (3 4) (4) ())
One way you can do that is by
(defn tails [coll]
(take (inc (count coll)) (iterate rest coll)))
(defn tails
[s]
(cons s (if-some [r (next s)]
(lazy-seq (tails r))
'(()))))
Yippee! Another one:
(defn tails [coll]
(if-let [s (seq coll)]
(cons coll (lazy-seq (tails (rest coll))))
'(())))
This is really just what reductions does under the hood. The best answer, by the way, is ez121sl's.
Is there a function that could replace subsequences? For example:
user> (good-fnc [1 2 3 4 5] [1 2] [3 4 5])
;; => [3 4 5 3 4 5]
I know that there is clojure.string/replace for strings:
user> (clojure.string/replace "fat cat caught a rat" "a" "AA")
;; => "fAAt cAAt cAAught AA rAAt"
Is there something similar for vectors and lists?
Does this work for you?
(defn good-fnc [s sub r]
(loop [acc []
s s]
(cond
(empty? s) (seq acc)
(= (take (count sub) s) sub) (recur (apply conj acc r)
(drop (count sub) s))
:else (recur (conj acc (first s)) (rest s)))))
Here is a version that plays nicely with lazy seq inputs. Note that it can take an infinite lazy sequence (range) without looping infinitely as a loop based version would.
(defn sq-replace
[match replacement sq]
(let [matching (count match)]
((fn replace-in-sequence [[elt & elts :as sq]]
(lazy-seq
(cond (empty? sq)
()
(= match (take matching sq))
(concat replacement (replace-in-sequence (drop matching sq)))
:default
(cons elt (replace-in-sequence elts)))))
sq)))
#'user/sq-replace
user> (take 10 (sq-replace [3 4 5] ["hello, world"] (range)))
(0 1 2 "hello, world" 6 7 8 9 10 11)
I took the liberty of making the sequence argument the final argument, since this is the convention in Clojure for functions that walk a sequence.
My previous (now deleted) answer was incorrect because this was not as trivial as I first thought, here is my second attempt:
(defn seq-replace
[coll sub rep]
(letfn [(seq-replace' [coll]
(when-let [s (seq coll)]
(let [start (take (count sub) s)
end (drop (count sub) s)]
(if (= start sub)
(lazy-cat rep (seq-replace' end))
(cons (first s) (lazy-seq (seq-replace' (rest s))))))))]
(seq-replace' coll)))
I need to take some amount of elements from a sequence based on some quantity rule. Here is a solution I came up with:
(defn take-while-not-enough
[p len xs]
(loop [ac 0
r []
s xs]
(if (empty? s)
r
(let [new-ac (p ac (first s))]
(if (>= new-ac len)
r
(recur new-ac (conj r (first s)) (rest s)))))))
(take-while-not-enough + 10 [2 5 7 8 2 1]) ; [2 5]
(take-while-not-enough #(+ %1 (%2 1)) 7 [[2 5] [7 8] [2 1]]) ; [[2 5]]
Is there any better way to achieve the same?
Thanks.
UPDATE:
Somebody posted that solution, but then removed it. It does the same is the answer that I accepted, but is more readable. Thank you, anonymous well-wisher!
(defn take-while-not-enough [reducer-fn limit data]
(->> (reductions reducer-fn 0 data) ; 1. the sequence of accumulated values
(map vector data) ; 2. paired with the original sequence
(take-while #(< (second %) limit)) ; 3. until a certain accumulated value
(map first))) ; 4. then extract the original values
My first thought is to view this problem as a variation on reduce and thus to break the problem into two steps:
count the number of items in the result
take that many from the input
I also took some liberties with the argument names:
user> (defn take-while-not-enough [reducer-fn limit data]
(take (dec (count (take-while #(< % limit) (reductions reducer-fn 0 data))))
data))
#'user/take-while-not-enough
user> (take-while-not-enough #(+ %1 (%2 1)) 7 [[2 5] [7 8] [2 1]])
([2 5])
user> (take-while-not-enough + 10 [2 5 7 8 2 1])
(2 5)
This returns a sequence and your examples return a vector, if this is important then you can add a call to vec
Something that would traverse the input sequence only once:
(defn take-while-not-enough [r v data]
(->> (rest (reductions (fn [s i] [(r (s 0) i) i]) [0 []] data))
(take-while (comp #(< % v) first))
(map second)))
Well, if you want to use flatland/useful, this is a kinda-okay way to use glue:
(defn take-while-not-enough [p len xs]
(first (glue conj []
(constantly true)
#(>= (reduce p 0 %) len)
xs)))
But it's rebuilding the accumulator for the entire "processed so far" chunk every time it decides whether to grow the chunk more, so it's O(n^2), which will be unacceptable for larger inputs.
The most obvious improvement to your implementation is to make it lazy instead of tail-recursive:
(defn take-while-not-enough [p len xs]
((fn step [acc coll]
(lazy-seq
(when-let [xs (seq coll)]
(let [x (first xs)
acc (p acc x)]
(when-not (>= acc len)
(cons x (step acc xs)))))))
0 xs))
Sometimes lazy-seq is straight-forward and self-explaining.
(defn take-while-not-enough
([f limit coll] (take-while-not-enough f limit (f) coll))
([f limit acc coll]
(lazy-seq
(when-let [s (seq coll)]
(let [fst (first s)
nacc (f acc fst)]
(when (< nxt-sd limit)
(cons fst (take-while-not-enough f limit nacc (rest s)))))))))
Note: f is expected to follow the rules of reduce.
I have a sequence s and a list of indexes into this sequence indexes. How do I retain only the items given via the indexes?
Simple example:
(filter-by-index '(a b c d e f g) '(0 2 3 4)) ; => (a c d e)
My usecase:
(filter-by-index '(c c# d d# e f f# g g# a a# b) '(0 2 4 5 7 9 11)) ; => (c d e f g a b)
You can use keep-indexed:
(defn filter-by-index [coll idxs]
(keep-indexed #(when ((set idxs) %1) %2)
coll))
Another version using explicit recur and lazy-seq:
(defn filter-by-index [coll idxs]
(lazy-seq
(when-let [idx (first idxs)]
(if (zero? idx)
(cons (first coll)
(filter-by-index (rest coll) (rest (map dec idxs))))
(filter-by-index (drop idx coll)
(map #(- % idx) idxs))))))
make a list of vectors containing the items combined with the indexes,
(def with-indexes (map #(vector %1 %2 ) ['a 'b 'c 'd 'e 'f] (range)))
#'clojure.core/with-indexes
with-indexes
([a 0] [b 1] [c 2] [d 3] [e 4] [f 5])
filter this list
lojure.core=> (def filtered (filter #(#{1 3 5 7} (second % )) with-indexes))
#'clojure.core/filtered
clojure.core=> filtered
([b 1] [d 3] [f 5])
then remove the indexes.
clojure.core=> (map first filtered)
(b d f)
then we thread it together with the "thread last" macro
(defn filter-by-index [coll idxs]
(->> coll
(map #(vector %1 %2)(range))
(filter #(idxs (first %)))
(map second)))
clojure.core=> (filter-by-index ['a 'b 'c 'd 'e 'f 'g] #{2 3 1 6})
(b c d g)
The moral of the story is, break it into small independent parts, test them, then compose them into a working function.
The easiest solution is to use map:
(defn filter-by-index [coll idx]
(map (partial nth coll) idx))
I like Jonas's answer, but neither version will work well for an infinite sequence of indices: the first tries to create an infinite set, and the latter runs into a stack overflow by layering too many unrealized lazy sequences on top of each other. To avoid both problems you have to do slightly more manual work:
(defn filter-by-index [coll idxs]
((fn helper [coll idxs offset]
(lazy-seq
(when-let [idx (first idxs)]
(if (= idx offset)
(cons (first coll)
(helper (rest coll) (rest idxs) (inc offset)))
(helper (rest coll) idxs (inc offset))))))
coll idxs 0))
With this version, both coll and idxs can be infinite and you will still have no problems:
user> (nth (filter-by-index (range) (iterate #(+ 2 %) 0)) 1e6)
2000000
Edit: not trying to single out Jonas's answer: none of the other solutions work for infinite index sequences, which is why I felt a solution that does is needed.
I had a similar use case and came up with another easy solution. This one expects vectors.
I've changed the function name to match other similar clojure functions.
(defn select-indices [coll indices]
(reverse (vals (select-keys coll indices))))
(defn filter-by-index [seq idxs]
(let [idxs (into #{} idxs)]
(reduce (fn [h [char idx]]
(if (contains? idxs idx)
(conj h char) h))
[] (partition 2 (interleave seq (iterate inc 0))))))
(filter-by-index [\a \b \c \d \e \f \g] [0 2 3 4])
=>[\a \c \d \e]
=> (defn filter-by-index [src indexes]
(reduce (fn [a i] (conj a (nth src i))) [] indexes))
=> (filter-by-index '(a b c d e f g) '(0 2 3 4))
[a c d e]
I know this is not what was asked, but after reading these answers, I realized in my own personal use case, what I actually wanted was basically filtering by a mask.
So here was my take. Hopefully this will help someone else.
(defn filter-by-mask [coll mask]
(filter some? (map #(if %1 %2) mask coll)))
(defn make-errors-mask [coll]
(map #(nil? (:error %)) coll))
Usage
(let [v [{} {:error 3} {:ok 2} {:error 4 :yea 7}]
data ["one" "two" "three" "four"]
mask (make-errors-mask v)]
(filter-by-mask data mask))
; ==> ("one" "three")