my own interpose function as an exercise - clojure

I'm resolving a 4Clojure exercise, this exercise asks you to build your own interpose function. My answer follows:
(fn my-interpose
([separator input] (my-interpose separator input nil))
([separator input result]
(if
(empty? input)
(reverse (rest result))
(my-interpose separator (rest input) (cons separator (cons (first input) result))))))
I'm doing these exercises to learn the language as I read a Clojure book. I will like to know the opinion about my code of people with an experience in the language. Could I avoid the reverse call? Are there any conventions I'm breaking hardly with this kind of code?

What you have is a good proper starting point :). Excellent work.
Starting with what you have you may want to:
Replace your recursive call with a call to recur because as written it will hit a stack overflow
(defn foo [stuff]
(dostuff ... )
(foo (rest stuff)))
becomes:
(defn foo [stuff]
(dostuff ...)
(recur (rest stuff)))
to avoid blowing the stack. this then often becomes:
(map dostuff stuff)
Replace the recustion entirely with the for function
(for [a one-list b another-list]
(dont-give-away-the-answer))

Yes you can avoid the reverse call,
(defn my-interpose [sep coll]
(when (seq coll)
(lazy-cat [(first coll) sep]
(my-interpose sep (rest coll)))))
as arthur suggest you can use recur in order to not to blow the stack but 99% of the time you don't need it.
EDIT:
This is a bit cleaner,
(defn my-interpose [sep coll]
(if-let [[first & rest] coll]
(lazy-cat [first sep] (my-interpose sep rest))))

Got to various answers using mapcat and for but in the end I found this:
#(rest (interleave (repeat %1) %2))
Where the first argument is the separator and the second is the collection. Just posting this answer for pure curiosity of other Clojure noobs like me.

Here is my solution, trying to rely on lower-level lisp or scheme-like functions.
(defn my-interpose[sep coll]
(letfn [(f [c]
(when-not (empty? c)
(cons sep (cons (first c)
(f (next c))))))]
(next (f coll))))

You may want to use map
(defn my-interpose [sep coll]
(rest (apply concat (map #(vector sep %) coll))))
or a directly a reduce and compute the answer as you go
(defn my-interpose [sep coll]
(rest (reduce #(conj %1 sep %2) [] coll)))
The idea behind this is to compute a sequence like (sep x0 sep x1 sep x2 ... sep xn) and then skip the first element to get (x0 sep x1 sep x2 ... xn).

Using just reduce and conj:
(defn my-interpose [sep a-seq]
(if (empty? a-seq)
a-seq
(reduce (fn [e1 e2] (if (empty? e1)
(conj e1 e2)
(conj e1 sep e2))) [] a-seq)))

Related

Clojure - Using recursion to find the number of elements in a list

I have written a function that uses recursion to find the number of elements in a list and it works successfully however, I don't particularly like the way I've written it. Now I've written it one way I can't seem to think of a different way of doing it.
My code is below:
(def length
(fn [n]
(loop [i n total 0]
(cond (= 0 i) total
:t (recur (rest i)(inc total))))))
To me it seems like it is over complicated, can anyone think of another way this can be written for comparison?
Any help greatly appreciated.
Here is a naive recursive version:
(defn my-count [coll]
(if (empty? coll)
0
(inc (my-count (rest coll)))))
Bear in mind there's not going to be any tail call optimization going on here so for long lists the stack will overflow.
Here is a version using reduce:
(defn my-count [coll]
(reduce (fn [acc x] (inc acc)) 0 coll))
Here is code showing some different solutions. Normally, you should use the built-in function count.
(def data [:one :two :three])
(defn count-loop [data]
(loop [cnt 0
remaining data]
(if (empty? remaining)
cnt
(recur (inc cnt) (rest remaining)))))
(defn count-recursive [remaining]
(if (empty? remaining)
0
(inc (count-recursive (rest remaining)))))
(defn count-imperative [data]
(let [cnt (atom 0)]
(doseq [elem data]
(swap! cnt inc))
#cnt))
(deftest t-count
(is (= 3 (count data)))
(is (= 3 (count-loop data)))
(is (= 3 (count-recursive data)))
(is (= 3 (count-imperative data))))
Here's one that is tail-call optimized, and doesn't rely on loop. Basically the same as Alan Thompson's first one, but inner functions are the best things. (And feel more idiomatic to me.) :-)
(defn my-count [sq]
(letfn [(inner-count [c s]
(if (empty? s)
c
(recur (inc c) (rest s))))]
(inner-count 0 sq)))
Just for completeness, here is another twist
(defn my-count
([data]
(my-count data 0))
([data counter]
(if (empty? data)
counter
(recur (rest data) (inc counter)))))

Grouping words and more

I'm working on a project to learn Clojure in practice. I'm doing well, but sometimes I get stuck. This time I need to transform sequence of the form:
[":keyword0" "word0" "word1" ":keyword1" "word2" "word3"]
into:
[[:keyword0 "word0" "word1"] [:keyword1 "word2" "word3"]]
I'm trying for at least two hours, but I know not so many Clojure functions to compose something useful to solve the problem in functional manner.
I think that this transformation should include some partition, here is my attempt:
(partition-by (fn [x] (.startsWith x ":")) *1)
But the result looks like this:
((":keyword0") ("word1" "word2") (":keyword1") ("word3" "word4"))
Now I should group it again... I doubt that I'm doing right things here... Also, I need to convert strings (only those that begin with :) into keywords. I think this combination should work:
(keyword (subs ":keyword0" 1))
How to write a function which performs the transformation in most idiomatic way?
Here is a high performance version, using reduce
(reduce (fn [acc next]
(if (.startsWith next ":")
(conj acc [(-> next (subs 1) keyword)])
(conj (pop acc) (conj (peek acc)
next))))
[] data)
Alternatively, you could extend your code like this
(->> data
(partition-by #(.startsWith % ":"))
(partition 2)
(map (fn [[[kw-str] strs]]
(cons (-> kw-str
(subs 1)
keyword)
strs))))
what about that:
(defn group-that [ arg ]
(if (not-empty arg)
(loop [list arg, acc [], result []]
(if (not-empty list)
(if (.startsWith (first list) ":")
(if (not-empty acc)
(recur (rest list) (vector (first list)) (conj result acc))
(recur (rest list) (vector (first list)) result))
(recur (rest list) (conj acc (first list)) result))
(conj result acc)
))))
Just 1x iteration over the Seq and without any need of macros.
Since the question is already here... This is my best effort:
(def data [":keyword0" "word0" "word1" ":keyword1" "word2" "word3"])
(->> data
(partition-by (fn [x] (.startsWith x ":")))
(partition 2)
(map (fn [[[k] w]] (apply conj [(keyword (subs k 1))] w))))
I'm still looking for a better solution or criticism of this one.
First, let's construct a function that breaks vector v into sub-vectors, the breaks occurring everywhere property pred holds.
(defn breakv-by [pred v]
(let [break-points (filter identity (map-indexed (fn [n x] (when (pred x) n)) v))
starts (cons 0 break-points)
finishes (concat break-points [(count v)])]
(mapv (partial subvec v) starts finishes)))
For our case, given
(def data [":keyword0" "word0" "word1" ":keyword1" "word2" "word3"])
then
(breakv-by #(= (first %) \:) data)
produces
[[] [":keyword0" "word0" "word1"] [":keyword1" "word2" "word3"]]
Notice that the initial sub-vector is different:
It has no element for which the predicate holds.
It can be of length zero.
All the others
start with their only element for which the predicate holds and
are at least of length 1.
So breakv-by behaves properly with data that
doesn't start with a breaking element or
has a succession of breaking elements.
For the purposes of the question, we need to muck about with what breakv-by produces somewhat:
(let [pieces (breakv-by #(= (first %) \:) data)]
(mapv
#(update-in % [0] (fn [s] (keyword (subs s 1))))
(rest pieces)))
;[[:keyword0 "word0" "word1"] [:keyword1 "word2" "word3"]]

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 Lazy Sequence Issue

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.

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)))