Re: Wisdom sought for functional iterative processing

2011-06-14 Thread Stuart Campbell
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

2011-06-14 Thread Alex Osborne
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

2011-06-14 Thread Matthew Phillips
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

2011-06-14 Thread Matthew Phillips
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

2011-06-14 Thread Matthew Phillips
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

2011-06-14 Thread Mark Engelberg
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

2011-06-14 Thread Matthew Phillips
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

2011-06-14 Thread Christian Schuhegger
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

2011-06-14 Thread Matthew Phillips
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

2011-06-13 Thread Matthew Phillips
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

2011-06-13 Thread gaz jones
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

2011-06-13 Thread Mark Engelberg
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