3

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?

rtruszk
  • 3,902
  • 13
  • 36
  • 53
eliocs
  • 18,511
  • 7
  • 40
  • 52

6 Answers6

5

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))
    
amalloy
  • 89,153
  • 8
  • 140
  • 205
Arthur Ulfeldt
  • 90,827
  • 27
  • 201
  • 284
  • this formats correctly in the preview and not when I save the edit? How do I fix this formatting? – Arthur Ulfeldt Sep 27 '11 at 19:29
  • Opened my mind with the 'map' usage, It's so obvious and at the same time so important. – eliocs Sep 27 '11 at 21:21
  • @amalloy thanks for fixing the formatting :) I just needed to use indents and not
     or  tags?
    – Arthur Ulfeldt Sep 27 '11 at 21:42
  • Yeah, getting code blocks into bulleted lists in markdown is a serious pain. I'm pretty sure it's possible to do it some way closer to what you were doing, but this is what I know. Also, I replaced your tags with \` marks because that's just so much easier to type. – amalloy Sep 27 '11 at 22:25
3

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))))
Hamza Yerlikaya
  • 49,047
  • 44
  • 147
  • 241
  • wow! lots to learn here, thanks! Could you please help me understand the if-let part. I mean, what condition are you evaluating for the if? I guess if you can't divide the collection into first and rest then false is returned? – eliocs Sep 27 '11 at 21:04
  • The condition he is testing is `coll`. If coll is nil, then the if fails, and nothing is `let`'ed. – amalloy Sep 27 '11 at 23:29
2

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.

eliocs
  • 18,511
  • 7
  • 40
  • 52
1

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

enrico.bacis
  • 30,497
  • 10
  • 86
  • 115
1

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))))
Julien Chastang
  • 17,592
  • 12
  • 63
  • 89
0

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)))
a.k
  • 1,035
  • 10
  • 27