Re: Wisdom sought for functional iterative processing
On 14 June 2011 12:37, Matthew Phillips mattp...@gmail.com wrote: The only way I can think of to write it in Clojure is: (reduce (fn [items op] (let [items1 (if (:delete op) (drop-index (:delete op) items) items)] (if (:insert op) (cons (:insert op) items1) items1))) items ops) i.e. I'm using a cascade of conditional let's. This isn't _too_ bad in this case, but you can image how unreadable this could get. How about something like this (borrowing from Mark's loop/recur suggestion above): (defn maybe-delete [items op] (if-let [index (:delete op)] (drop-index index items) items)) (defn maybe-insert [items op] (if-let [new-item (:insert op)] (cons new-item items) items)) (loop [items items ops ops] (if-not ops items (let [op (first op)] (recur (- items (maybe-delete op) (maybe-insert op)) (next ops) Looks more verbose on the surface, but I'm sure you could abstract away some common bits of maybe-delete and maybe-insert using a macro. Regards, Stuart -- 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 Note that posts from new members are moderated - please be patient with your first post. 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
Re: Wisdom sought for functional iterative processing
Matthew Phillips mattp...@gmail.com writes: The only way I can think of to write it in Clojure is: (reduce (fn [items op] (let [items1 (if (:delete op) (drop-index (:delete op) items) items)] (if (:insert op) (cons (:insert op) items1) items1))) items ops) i.e. I'm using a cascade of conditional let's. This isn't _too_ bad in this case, but you can image how unreadable this could get. To avoid a cascade of lets I've occasionally defined a function called conj-if (or disj-if or assoc-if...). Like so: (defn conj-if [coll test x] (if test (conj coll x) coll)) (fn [items op] (- (conj-if (foo? op) one) (conj-if (bar? op) two))) (conj-if (baz? op) three))) That could be generalized, but I've so far never gotten into a situation where I've felt the need to. (defn call-if [x test f [args]]; needs a better name (if test (apply f x args) x)) (fn [items op] (- (call-if (foo? op) assoc foo 1) (call-if (bar? op) dissoc bar) (call-if (baz? op) empty))) On the other hand the cascading lets are really not that bad especially if you pull any complex predicate or value-manipulation logic out into separate functions: (fn [items op] (let [items (if (foo? op) (assoc items foo 1) items) items (if (bar? op) (dissoc items bar) items) items (if (baz? op) (empty items) items)] items)) -- 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 Note that posts from new members are moderated - please be patient with your first post. 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
Re: Wisdom sought for functional iterative processing
On Jun 14, 12:05 pm, gaz jones gareth.e.jo...@gmail.com wrote: if i was writing the java i would probably do a tell dont ask refactoring so that the operations had an applyTo method: ListItem items = initialItems (); for (Op op : operations) { op.applyTo(items); } not sure what your op data structure is, but i would image you could translate that to the clojure code also -- is that reasonable? I think you're right that the above is a better OO design, but that was really just a Java translation of the sort of think I'd like to do in Clojure (minus the mutable list). The ops in this case have abstract meaning (they are [source target delta] tuples describing list transformations), and attaching methods to them (presumably as fn's) in this case would be weird I think, as well as requiring a switch from tuples to maps or records. -- 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 Note that posts from new members are moderated - please be patient with your first post. 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
Re: Wisdom sought for functional iterative processing
On Jun 14, 12:30 pm, Mark Engelberg mark.engelb...@gmail.com wrote: On Mon, Jun 13, 2011 at 7:37 PM, Matthew Phillips mattp...@gmail.com wrote: ListItem items = initialItems (); for (Op op : operations) { if (op.requiresDelete ()) items.remove (op.indexToDelete ()); if (op.requiresAdd ()) items.add (op.indexToAdd (), op.newItem ()); } One way to transform this is loop-recur. (loop [items initialitems, ops (seq operations)] (if-not ops items (let [op (first ops)] ;; Now you can write the logic pretty much the way you did in java (cond (.requiresDelete op) (recur (.remove items (.op indexToDelete)) (next ops)) (.requiresAdd op) (recur (.add items (.op indexToAdd) (.op newItem)) (next ops) Yes. I agree that can work, and that's what I've done in some other situations, but it has the downside of lots of recur points sprinkled around the loop body, which I think makes it almost as hard to reason about as having lots of return statements in a Java method. For small problems like this it's just as good/bad as my solution, but what I'm really looking for is a good general idiom to use whenever this sort of thing comes up. -- 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 Note that posts from new members are moderated - please be patient with your first post. 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
Re: Wisdom sought for functional iterative processing
On Jun 14, 4:40 pm, Alex Osborne a...@meshy.org wrote: Matthew Phillips mattp...@gmail.com writes: The only way I can think of to write it in Clojure is: (reduce (fn [items op] (let [items1 (if (:delete op) (drop-index (:delete op) items) items)] (if (:insert op) (cons (:insert op) items1) items1))) items ops) i.e. I'm using a cascade of conditional let's. This isn't _too_ bad in this case, but you can image how unreadable this could get. To avoid a cascade of lets I've occasionally defined a function called conj-if (or disj-if or assoc-if...). Like so: (defn conj-if [coll test x] (if test (conj coll x) coll)) (fn [items op] (- (conj-if (foo? op) one) (conj-if (bar? op) two))) (conj-if (baz? op) three))) Yes, I have thought about using - to chain transformations together, in fact it's a little like what I imagine the ST monad (or is it the State monad?) does by chaining a series of apparent-variable- assignments together. I like this idea, but something about writing xxx-if functions doesn't seem right, perhaps due to the number of xxx's there might be. Perhaps I need a do-if fn that takes a target, predicate and a transform fn, and applies the transform when pred is true. Will have a think about that. On the other hand the cascading lets are really not that bad especially if you pull any complex predicate or value-manipulation logic out into separate functions: (fn [items op] (let [items (if (foo? op) (assoc items foo 1) items) items (if (bar? op) (dissoc items bar) items) items (if (baz? op) (empty items) items)] items)) Interesting, it hadn't occurred to me that I could just keep shadowing items in the same let. Usually I think shadowing is a bad idea, but perhaps it's better than items1, items2, etc. Thanks! -- 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 Note that posts from new members are moderated - please be patient with your first post. 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
Re: Wisdom sought for functional iterative processing
On Tue, Jun 14, 2011 at 7:41 PM, Matthew Phillips mattp...@gmail.com wrote: Yes. I agree that can work, and that's what I've done in some other situations, but it has the downside of lots of recur points sprinkled around the loop body, which I think makes it almost as hard to reason about as having lots of return statements in a Java method. For small problems like this it's just as good/bad as my solution, but what I'm really looking for is a good general idiom to use whenever this sort of thing comes up. To summarize what others have already pointed out, the main problem with your approach is that you're trying to do to much in the loop. It's convertible to Clojure, but clunky to read. The key to making this readable is to focus on writing a separate function of the form: processOp: items op - items If you have such a function, the loop is trivial (or use reduce as in your original post). It's hard to know the cleanest way to write such a function without knowing the details of your problem, but that's really the key. -- 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 Note that posts from new members are moderated - please be patient with your first post. 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
Re: Wisdom sought for functional iterative processing
On Jun 15, 11:51 am, Mark Engelberg mark.engelb...@gmail.com wrote: On Tue, Jun 14, 2011 at 7:41 PM, Matthew Phillips mattp...@gmail.com wrote: Yes. I agree that can work, and that's what I've done in some other situations, but it has the downside of lots of recur points sprinkled around the loop body, which I think makes it almost as hard to reason about as having lots of return statements in a Java method. For small problems like this it's just as good/bad as my solution, but what I'm really looking for is a good general idiom to use whenever this sort of thing comes up. To summarize what others have already pointed out, the main problem with your approach is that you're trying to do to much in the loop. It's convertible to Clojure, but clunky to read. The key to making this readable is to focus on writing a separate function of the form: processOp: items op - items If you have such a function, the loop is trivial (or use reduce as in your original post). Wouldn't such a processOp be the same as the anonymous fn in my reduce- based example? I'm really asking how to write that fn better or, more generally, what is a good idiom for threading conditional transformations. I think I'll pursue the - (or -) based approach and see where it leads, because it seems to fit best with my mental model of pipelining data transforms. And it's entirely possible that I'm over-thinking this, but I treat it as part of learning how to use this tool in its most effective way, rather than just mentally translating Java into Clojure ;) -- 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 Note that posts from new members are moderated - please be patient with your first post. 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
Re: Wisdom sought for functional iterative processing
Ah, sorry, perhaps I misunderstand you, but if you use multimethods (defmulti) in Clojure you do not need to attach methods to anything. The defmulti will allow you dispatch-on-type based on the key which is present in the map (e.g. if :delete is present or if :insert is present). The nice thing about multimethods the lisp style is that they are additive, e.g. you do not have to change anything in your op structure. On Jun 15, 4:38 am, Matthew Phillips mattp...@gmail.com wrote: On Jun 14, 12:05 pm, gaz jones gareth.e.jo...@gmail.com wrote: if i was writing the java i would probably do a tell dont ask refactoring so that the operations had an applyTo method: ListItem items = initialItems (); for (Op op : operations) { op.applyTo(items); } not sure what your op data structure is, but i would image you could translate that to the clojure code also -- is that reasonable? I think you're right that the above is a better OO design, but that was really just a Java translation of the sort of think I'd like to do in Clojure (minus the mutable list). The ops in this case have abstract meaning (they are [source target delta] tuples describing list transformations), and attaching methods to them (presumably as fn's) in this case would be weird I think, as well as requiring a switch from tuples to maps or records. -- 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 Note that posts from new members are moderated - please be patient with your first post. 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
Re: Wisdom sought for functional iterative processing
On Jun 15, 12:41 pm, Christian Schuhegger christian.schuheg...@gmail.com wrote: Ah, sorry, perhaps I misunderstand you, but if you use multimethods (defmulti) in Clojure you do not need to attach methods to anything. The defmulti will allow you dispatch-on-type based on the key which is present in the map (e.g. if :delete is present or if :insert is present). The nice thing about multimethods the lisp style is that they are additive, e.g. you do not have to change anything in your op structure. A very good point. I think it would be overkill here, but you're absolutely right that multimethods would be an elegant way to do it if I went that route. Another aspect I need to get used to considering, moving from OO :/ -- 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 Note that posts from new members are moderated - please be patient with your first post. 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
Wisdom sought for functional iterative processing
Hello all, I've been programming Clojure now for long enough that I'm starting to think I'm at the point of being fluent … almost. One area that keeps tripping me up is iteratively processing a data structure when the processing is highly conditional and may take several overlapping paths. An example might help: Example (simplified from my current real problem): Say I need to iterate through a seq of encoded list operations and apply them to a list. Some ops will require me to remove an element from the list, some will require me to add an item to list, and some will require both. The way I'd write it in Java would be something like: ListItem items = initialItems (); for (Op op : operations) { if (op.requiresDelete ()) items.remove (op.indexToDelete ()); if (op.requiresAdd ()) items.add (op.indexToAdd (), op.newItem ()); } You could imagine arbitrarily complex conditions for the transforms. The only way I can think of to write it in Clojure is: (reduce (fn [items op] (let [items1 (if (:delete op) (drop-index (:delete op) items) items)] (if (:insert op) (cons (:insert op) items1) items1))) items ops) i.e. I'm using a cascade of conditional let's. This isn't _too_ bad in this case, but you can image how unreadable this could get. I have a vague idea that in Haskell you might use the ST monad to approximate updating data structure in place. Clojure has a monad library, so would this make sense here? Thanks for any enlightenment anyone is able to give. Matthew. -- 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 Note that posts from new members are moderated - please be patient with your first post. 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
Re: Wisdom sought for functional iterative processing
if i was writing the java i would probably do a tell dont ask refactoring so that the operations had an applyTo method: ListItem items = initialItems (); for (Op op : operations) { op.applyTo(items); } not sure what your op data structure is, but i would image you could translate that to the clojure code also -- is that reasonable? On Mon, Jun 13, 2011 at 9:37 PM, Matthew Phillips mattp...@gmail.com wrote: Hello all, I've been programming Clojure now for long enough that I'm starting to think I'm at the point of being fluent … almost. One area that keeps tripping me up is iteratively processing a data structure when the processing is highly conditional and may take several overlapping paths. An example might help: Example (simplified from my current real problem): Say I need to iterate through a seq of encoded list operations and apply them to a list. Some ops will require me to remove an element from the list, some will require me to add an item to list, and some will require both. The way I'd write it in Java would be something like: ListItem items = initialItems (); for (Op op : operations) { if (op.requiresDelete ()) items.remove (op.indexToDelete ()); if (op.requiresAdd ()) items.add (op.indexToAdd (), op.newItem ()); } You could imagine arbitrarily complex conditions for the transforms. The only way I can think of to write it in Clojure is: (reduce (fn [items op] (let [items1 (if (:delete op) (drop-index (:delete op) items) items)] (if (:insert op) (cons (:insert op) items1) items1))) items ops) i.e. I'm using a cascade of conditional let's. This isn't _too_ bad in this case, but you can image how unreadable this could get. I have a vague idea that in Haskell you might use the ST monad to approximate updating data structure in place. Clojure has a monad library, so would this make sense here? Thanks for any enlightenment anyone is able to give. Matthew. -- 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 Note that posts from new members are moderated - please be patient with your first post. 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 -- 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 Note that posts from new members are moderated - please be patient with your first post. 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
Re: Wisdom sought for functional iterative processing
On Mon, Jun 13, 2011 at 7:37 PM, Matthew Phillips mattp...@gmail.com wrote: ListItem items = initialItems (); for (Op op : operations) { if (op.requiresDelete ()) items.remove (op.indexToDelete ()); if (op.requiresAdd ()) items.add (op.indexToAdd (), op.newItem ()); } One way to transform this is loop-recur. (loop [items initialitems, ops (seq operations)] (if-not ops items (let [op (first ops)] ;; Now you can write the logic pretty much the way you did in java (cond (.requiresDelete op) (recur (.remove items (.op indexToDelete)) (next ops)) (.requiresAdd op) (recur (.add items (.op indexToAdd) (.op newItem)) (next ops) -- 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 Note that posts from new members are moderated - please be patient with your first post. 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