DavidH wrote:
> In the code I'm writing, I seem to run into a certain pattern a lot.
> Often enough anyway that I thought there must be a different way of
> doing it - otherwise there would be a macro or function for it.
> 
> The gist is that a function is applied to each member of a sequence,
> like map, except that the function also takes a changing state.  The
> change to the state is described by a second function.
> 
> Here's a function describing the pattern.
> 
> (defn stateful-map [fn-item fn-state start-state start-items]
>   (loop [state start-state
>          items start-items
>          result nil]
>     (if (nil? items)
>       (reverse result)
>       (let [item (first items)
>             new-item (fn-item state item)
>             new-state (fn-state state item)]
>         (recur new-state (rest items) (cons new-item result))))))
> 
> So the question is, is there a better way of doing this?

(NB all code off the top of my head and untested so may need tweaks)

You might want to consider using the reduce function for stuff like
this, and holding state and result in the first argument. I commonly run
into the pattern where I need to "map" to a hash map, which is a classic
use of reduce that doesn't "reduce" anything in the sense that the
result is more compact:

(reduce
  (fn [m { k :key  :as v }]
    (assoc m k v))
  {}
  '( { :key 1520 :name "some obj" }  { :key 420 :name "other obj" } ))

It might get a little cumbersome if there's a lot of internal state to
maintain, in which case I'd go for either the lazy-cons macro or use the
fnseq function explicitly, whichever is easier in practice. (usually
lazy-cons) The idea is that the state is maintained in the closure that
gets evaluated for the "rest" of the resulting cons. In your example:

(defn stateful-map [fn-item fn-state state coll]
  (if (empty? coll)
    nil
    (let [item (first coll)]
      (lazy-cons
        (fn-item state item)
        ; this part is lazy and therefore not really recursive:
        (stateful-map fn-item fn-state
          (fn-state state item) (rest coll))))))

This example can be optimised a little more by using an inner fn, which
should make it a little shorter still.

Hope that helps,
~phil

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To post to this group, send email to clojure@googlegroups.com
To unsubscribe from this group, send email to 
clojure+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to