Clojure Lazy Sequence Issue - clojure

I'm working on 4clojure problems and a similar issue keeps coming up. I'll write a solution that works for all but one of the test cases. It's usually the one that is checking for lazy evaluation. The solution below works for all but the last test case. I've tried all kinds of solutions and can't seem to get it to stop evaluating until integer overflow. I read the chapter on lazy sequences in Joy of Clojure, but I'm having a hard time implementing them. Is there a rule of thumb I'm forgetting, like don't use loop or something like that?
; This version is non working at the moment, will try to edit a version that works
(defn i-between [p k coll]
(loop [v [] coll coll]
(let [i (first coll) coll (rest coll) n (first coll)]
(cond (and i n)
(let [ret (if (p i n) (cons k (cons i v)) (cons i v))]
(recur ret coll))
i
(cons i v )
:else v))))
Problem 132
Ultimate solution for those curious:
(fn i-between [p k coll]
(letfn [(looper [coll]
(if (empty? coll) coll
(let [[h s & xs] coll
c (cond (and h s (p h s))
(list h k )
(and h s)
(list h )
:else (list h))]
(lazy-cat c (looper (rest coll))))
))] (looper coll)))

When I think about lazy sequences, what usually works is thinking about incremental cons'ing
That is, each recursion step only adds a single element to the list, and of course you never use loop.
So what you have is something like this:
(cons (generate first) (recur rest))
When wrapped on lazy-seq, only the needed elements from the sequence are realized, for instance.
(take 5 (some-lazy-fn))
Would only do 5 recursion calls to realize the needed elements.
A tentative, far from perfect solution to the 4clojure problem, that demonstrates the idea:
(fn intercalate
[pred value col]
(letfn [(looper [s head]
(lazy-seq
(if-let [sec (first s)]
(if (pred head sec)
(cons head (cons value (looper (rest s) sec)))
(cons head (looper (rest s) sec)))
(if head [head] []))))]
(looper (rest col) (first col))))
There, the local recursive function is looper, for each element tests if the predicate is true, in that case realizes two elements(adds the interleaved one), otherwise realize just one.
Also, you can avoid recursion using higher order functions
(fn [p v xs]
(mapcat
#(if (p %1 %2) [%1 v] [%1])
xs
(lazy-cat (rest xs) (take 1 xs))))
But as #noisesmith said in the comment, you're just calling a function that calls lazy-seq.

Related

Clojure filter method without standard clojure functions

I need to make filter method, but i can't return method name as argument, not as result.
In my case, i need to input odd? method as argument and call recursion.
I can use only this construction:
(defn my-filter [p xn])
My code:
(defn my-filter [p xs]
(if (p (first xs))
(cons (first xs) (recur p (next xs)))
(recur p (next xs) )))
(my-filter odd? '(1 2 3 4 5))
Error: IllegalArgumentException Argument must be an integer: clojure.core/even? (core.clj:1372)
As i can see, where recursion is called, arguments are calculating result, instead of call recursion with odd? and (next xs) arguments
Two issues need attention. Or maybe only one issue, if you don't need to handle very long lists. 1) The function does not notice when the inputs are exhausted. Open a REPL and try (odd? nil) and you will see what happens! 2) If you try the function on a really long list, you might get a StackOverflow. The clojure.org guide for recursion has an example of how to avoid that problem - actually it illustrates solutions to both problems: https://clojure.org/guides/learn/flow#_recursion
Your code does not compile:
Syntax error (UnsupportedOperationException) compiling recur at ... .
Can only recur from tail position
You have to replace the recurs with explicit recursive calls to my-filter:
(defn my-filter [p xs]
(if (p (first xs))
(cons (first xs) (my-filter p (next xs)))
(my-filter p (next xs))))
Now it compiles, but ...
(my-filter odd? [])
Execution error (IllegalArgumentException) at ...
Argument must be an integer:
You need to check that the sequence argument xs is not empty before doing anything else with it:
(defn my-filter [p xs]
(when (seq xs)
(if (p (first xs))
(cons (first xs) (my-filter p (rest xs)))
(my-filter p (rest xs)))))
The when evaluates to nil if the condition fails. The nil, called on to be a sequence, behaves as an empty one. So ...
(my-filter odd? [])
=> nil
(my-filter odd? (range 10))
=> (1 3 5 7 9)
It works. However, it evaluates (first xs) twice, and mentions (my-filter p (rest xs)) twice. Factoring these out, we get
(defn my-filter [p xs]
(when (seq xs)
(let [head (first xs)
tail (my-filter p (rest xs))]
(if (p head) (cons head tail) tail))))
This uses direct recursion. So it runs out of stack on a long sequence:
(count (my-filter odd? (range 10000)))
Execution error (StackOverflowError) at ...
Wrapping the recursion in lazy-seq flattens the evaluation, devolving it to whatever explores the sequence:
(defn my-filter [p xs]
(lazy-seq
(when (seq xs)
(let [head (first xs)
tail (my-filter p (rest xs))]
(if (p head) (cons head tail) tail)))))
Now ...
(count (my-filter odd? (range 10000)))
=> 5000
If you want an eager version, you had better build the returned sequence as a vector:
(defn eager-filter [p coll]
(loop [answer [], coll (seq coll)]
(if-let [[x & xs] coll]
(recur
(if (p x) (conj answer x) answer)
xs)
(sequence answer))))
This won't run out of stack:
(count (eager-filter odd? (range 10000)))
=> 5000
But it can't handle an endless sequence:
(first (eager-filter odd? (range)))
Process finished with exit code 137 (interrupted by signal 9: SIGKILL)
I had to kill the process.
I really like this solution, therefore I share with you. Very simple. (I know, that is not exactly what was the question but different approach.)
(def data (vec (range 10)))
Map implementation
(defn -map [f coll]
(reduce
(fn [acc v]
(conj acc (f v)))
[]
coll))
Filter implementation
(defn -filter [f coll]
(reduce
(fn [acc v]
(if (f v)
(conj acc v))
[]
coll))
Example usage
(->> data
(-map inc)
(-filter odd?))

Making a take function myself

I'm trying to make a take function myself, but this appears to be giving a stack overflow, any idea what may be causing it?
(defn my-take-plus [n Lst LstAcc count]
(let [LstVec (into [] Lst)]
(cond (= count n) LstAcc
:else
(do
(conj LstAcc (first LstVec))
(inc count)
(my-take-plus n (apply list(rest LstVec)) LstAcc count)
)
)
)
)
(defn my-take [n Lst]
(my-take-plus n Lst [] 0)
)
also, there is one more 'clojurish' way to do this:
(defn my-take [n data]
(when (and (pos? n) (seq data))
(lazy-seq
(cons (first data)
(my-take (dec n) (rest data))))))
this one is lazy, and also prevents stack overflow.. Moreover, as far as i remember, the clojure.core/take is implemented in a similar way
I would consider using a loop/recur strategy so that Clojure does tail-call optimization (TCO) to prevent a Stack Overflow.
(defn take' [n coll]
(loop [n n
acc []
coll coll]
(cond
(empty? coll) acc
((comp not pos?) n) acc
:else (recur (dec n) (conj acc (first coll)) (rest coll)))))
In your example, I would've considered using an if since you only had to conditional branches. cond is generally used more like a case statement.

Why is my lazy recursive Clojure function being realized?

I'm trying to solve a 4Clojure problem (sequence reductions), and I've hit a wall. The problem is to reimplement the reductions function.
It seems to me like this function should return a lazy sequence, but it doesn't - evaluating (take 5 (redux + (range))) results in an infinite loop.
Here's my code:
(defn redux
([f coll]
(redux f (first coll) (rest coll)))
([f val coll]
((fn red [val coll s]
(if (empty? coll)
s
(lazy-seq
(let [val (f val (first coll))]
(red val
(rest coll)
(conj s val))))))
val coll [val])))
Why is this function not returning a lazy sequence?
There are a few misconceptions in the code. noisesmith pointed out on #clojurians chat (and Josh's comment stated as well) the following points:
There is no step in the above function where you can evaluate the head and not the tail of a list.
It does an immediate self recursion, and to get the n+1 element, you need to do the recursive call.
lazy-seq should always have a call to cons or some similar function that lets you return the next item of the list without recurring.
conj is never lazy, and vectors are never lazy.
You cannot append to a list without realizing the entire thing.
I modified the code to the following:
(fn redux
([f coll]
(redux f (first coll) (rest coll)))
([f val coll]
(cons val
((fn red [val coll]
(lazy-seq
(when-not (empty? coll)
(let [val (f val (first coll))]
(cons val (red val (rest coll)))))))
val coll))))
Note the use of cons instead of conj.

review my beginners clojure reverse function [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 8 years ago.
Improve this question
I am just starting out in my clojure journey and I wonder if anybody can point out my beginners mistakes in my function below that simply reverses a list. I know that there is already a reverse function so this is purely for learning.
(defn rev
([l]
(if (= (count l) 1) l (rev '() l)))
([l orig]
(if (= (count orig) 0)
l
(rev (conj l (first orig)) (rest orig)))))
In my defence, it does work but what I am finding myself doing a lot in clojure is overloading the arguments list to take into account when I need a working list like in this example where I conj new items onto.
First, it's a really good idea to look at the existing implementation of reverse function first:
(defn reverse [coll]
(reduce conj () coll))
The main difference from your code is that existing implementation of reverse uses higher-order function reduce. It's a good practice to use higher-order functions instead of recursion wherever possible.
--
But let's assume that your goal is to learn recursion. Here is how I would've written it:
(defn rev
([coll]
(rev coll ()))
([coll acc]
(if-let [[h & ts] (seq coll)]
(recur ts (conj acc h))
acc)))
Let's have a closer look at my code.
First, I'm using if-let and seq to check that coll is a non-empty collection.
Then I'm using destructuring to get the first element of the given collection and the rest of it.
In other words, my construction
(if-let [[h & ts] (seq coll)]
(recur ts (conj acc h))
acc)
could be rewritten with if, let, first and rest:
(if-not (empty? coll)
(let [h (first coll)
ts (rest coll)]
(recur ts (conj acc h)))
acc)
which is almost what you wrote yourself.
The last important thing is that I'm using recur instead of calling rev directly.
It allow clojure compiler to perform tail recursion optimization.
You should also consider using loop instead of creating an overloaded function, unless you want to make two-arguments form public:
(defn rev [coll]
(loop [coll coll
acc ()]
(if-let [[h & ts] (seq coll)]
(recur ts (conj acc h))
acc)))
--
So, there is a lot of things to improve in your code. But I can see only three real mistakes there:
you should use recur;
there is no point in checking (= (count l) 1);
you should use empty? instead of (= (count orig) 0).
Here I fixed these two mistakes in your code:
(defn rev
([l]
(rev '() l))
([l orig]
(if (empty? orig)
l
(recur (conj l (first orig)) (rest orig)))))
If you don't want to expose the function with other arities, define and employ them locally:
(defn rev [l]
((fn rev2 [l orig]
(if (empty? orig)
l
(rev2 (conj (first orig) l) (rest orig))))
() l))
You may find it easier to use let or letfn:
(defn rev [l]
(letfn [(rev2 [l orig]
(if (empty? orig)
l
(rev2 (conj l (first orig)) (rest orig))))]
(rev2 () l)))
And, by the way, ...
Don't count the sequence if you just want to know whether there is
anything in it. Use seq or empty?.
Get rid of the special case of 1 element sequences: it isn't special.
() doesn't need quoting.
And, of course, we can and ought to use recur instead of the recursive call to rev2, to avoid blowing the stack on long sequences:
(defn rev [l]
(letfn [(rev2 [l orig]
(if (empty? orig)
l
(recur (conj l (first orig)) (rest orig))))]
(rev2 () l)))

Clojure: What is wrong with my implementation of flatten?

I've been working through problems on 4Clojure today, and I ran into trouble on Problem 28, implementing flatten.
There are a couple of definite problems with my code.
(fn [coll]
((fn flt [coll res]
(if (empty? coll)
res
(if (seq? (first coll))
(flt (into (first coll) (rest coll)) res)
(flt (rest coll) (cons (first coll) res))))) coll (empty coll)))
I could use some pointers on how to think about a couple of problems.
How do I make sure I'm not changing the order of the resulting list? cons and conj both add elements wherever it is most efficient to add elements (at the beginning for lists, at the end for vectors, etc), so I don't see how I'm supposed to have any control over this when working with a generic sequence.
How do I handle nested sequences of different types? For instance, an input of '(1 2 [3 4]) will will output ([3 4] 2 1), while an input of [1 2 '(3 4)] will output (4 3 2 1)
Am I even approaching this from the 'right' angle? Should I use a recursive inner function with an accumulator to do this, or am I missing something obvious?
You should try to use HOF (higher order functions) as much as possible: it communicates your intent more clearly and it spares you from introducing subtle low-level bugs.
(defn flatten [coll]
(if (sequential? coll)
(mapcat flatten coll)
(list coll)))
Regarding your questions about lists and vectors. As you might see in tests, output is list. Just make correct abstraction. Fortunately, clojure already has one, called sequence.
All you need is first, rest and some recursive solution.
One possible approach:
(defn flatten [[f & r]]
(if (nil? f)
'()
(if (sequential? f)
(concat (flatten f) (flatten r))
(cons f (flatten r)))))
Here's how to do it in a tail call optimised way, within a single iteration, and using the least amount of Clojure.core code as I could:
#(loop [s % o [] r % l 0]
(cond
(and (empty? s) (= 0 l))
o
(empty? s)
(recur r
o
r
(dec l))
(sequential? (first s))
(recur (first s)
o
(if (= 0 l)
(rest s)
r)
(inc l))
:else
(recur (rest s)
(conj o (first s))
r
l)))