6

i'm working through SICP - one exercise is to implement foreach (doseq). This is an academic exercise. In clojure, this is what I came up with:

(defn for-each [proc, items]
  (if (empty? items) nil
      (do
        (proc (first items))
        (recur proc (rest items)))))

but, i'm a little murky about if do is cheating, because do is a special form in clojure and i don't think anything like that has been introduced yet in SICP. is there a more minimalist answer?

Here's another attempt which only executes proc on the last element:

(defn for-each-2 [proc, items]
  (let [f (first items)
        r (rest items)]
    (if (empty? r)
      (proc f)
      (recur proc r))))
Óscar López
  • 232,561
  • 37
  • 312
  • 386
Dustin Getz
  • 21,282
  • 15
  • 82
  • 131
  • You're fine. SICP is being tricksy here. See the very small text of footnote 3 in: http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-20.html#call_footnote_Temp_323. Anytime you're doing a `cond` in SICP, you've got an implicit `begin` in play for each clause of the `cond`. And `begin` in SICP is pretty much `do` in Clojure. – dyoo Feb 27 '12 at 04:29

2 Answers2

3

Use doseq and you're all set. For example:

(doseq [e '(1 2 3)]
       (prn e))

Will print:

1
2
3
nil

EDIT :

If you want to implement for-each by hand and using as few special forms as possible, here's another alternative, although it ends up being almost as short as yours:

(defn for-each [f l]
  (cond (empty? l) nil
        :else (do (f (first l)) 
                  (recur f (rest l)))))

Interestingly, the same procedure could have been written more succinctly in Scheme, the Lisp dialect used in SICP:

(define (for-each f l)
  (cond ((null? l) null)
        (else (f (first l))
              (for-each f (rest l)))))
Óscar López
  • 232,561
  • 37
  • 312
  • 386
  • yes, i want to implement that with as few special forms as possible. – Dustin Getz Feb 26 '12 at 19:35
  • @DustinGetz I updated my answer with another option, but really that's about as short as it can get – Óscar López Feb 26 '12 at 20:14
  • 1
    And no, at least in Clojure you can't avoid `do` for indicating that more than one statement needs to be executed in sequence – Óscar López Feb 26 '12 at 20:15
  • 2
    ah. so the difference is that in scheme, the else clause is a list of expressions to 'do'. clojure makes this explicit, hence the required dependency on some sort of special form that can execute a list of statements. thanks! – Dustin Getz Feb 26 '12 at 20:38
1

Here is my attempt. It just carries function execution in an inner loop.

(defn for-each [fun, xs]
  (loop [fun fun
         xs xs
         action nil]
    (if (first xs)
      (recur fun (rest xs) (fun (first xs)))
      xs)))
4e6
  • 10,696
  • 4
  • 52
  • 62
  • thats quite clever - it exploits the fact that the bracketed part of a loop is a list of bindings executed in order. now that i understand, this is equivalent complexity to the `do` approach. – Dustin Getz Feb 26 '12 at 21:00
  • I'm glad you found it helpful. I thought once again and decided `action nil` would be fine. – 4e6 Feb 26 '12 at 21:25