Re: Changing State Pattern - Better Options?

2009-01-18 Thread Phil Jordan

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
-~--~~~~--~~--~--~---



Re: FindBugs run on Clojure source code

2008-11-15 Thread Phil Jordan

Hi Attila,

I can't comment on the other issues, but:

Attila Szegedi wrote:
 2. Keyword and Ref define compareTo, but don't redefine equals (and  
 hashCode) to be consistent with it. It ain't necessarily a problem if  
 you know what you're doing, but since they're public it's usually a  
 good rule of thumb to have equals consistent with compareTo (and then  
 hashCode consistent with equals).

This complaint makes no sense to me. Keywords and Refs have identity
equality semantics. No two keywords with the same name can exist,
therefore if two keywords are equal, they are the same object. Using a
pointer comparison is therefore semantically correct, and has the nice
side effect of being faster. Likewise, ref compareTo is based on
comparing the refs' unique IDs. Given that they're *unique*, two refs
are equal iff they are identical.

~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 [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/clojure?hl=en
-~--~~~~--~~--~--~---



Re: Recent breakage (regression?) of macros

2008-10-27 Thread Phil Jordan

Hi Rich,

Thanks for the quick reply! I'm probably just being slow here, for which
I apologise, but some questions remain:

Rich Hickey wrote:
 On Oct 26, 3:49 pm, Phil Jordan [EMAIL PROTECTED] wrote:
 First of all, is the breakage intentional?
 
 Yes. You can no longer embed unreadable objects in code, in this case
 fn constants. This is to support ahead-of-time compilation - every
 constant must have a readable print representation.

Okay, I *think* I understand what you're saying. Those fns are instances
of a class which has been compiled while processing the (def mylocals
..) form, but at compile time, the mylocals var hasn't been
*initialised* - that happens at load time, where instances of those fn
classes are created. Those instances could be closures which have local
state and can't be baked in.

It works for symbols because they're stateless, unique atoms.

Am I on the right track here?

 I've added an unreadable #... form, made it the default for print-
 method, and added a check for unreadable forms in in-code constants
 and throw during compilation, in order to move the detection of this
 problem to compilation time vs. class initialization time.

Okay, I'm now getting the more sane error of:

java.lang.RuntimeException: Can't embed unreadable object in code:
#[EMAIL PROTECTED]

which certainly helps to localise the issue should it crop up again in
future. However, I'm still not clear on how to best (correctly,
idiomatically) write this sort of macro where locals named elsewhere
are bound to functions defined at compile time. I currently see these
possibilities:


(1) let the macro expand into an eval on the block's body. This works
for anything of course, but I view runtime eval as a kind of last resort.


(2) expand into var lookups, like so:

(defmacro letlocals [names fns  body]
(list*
'let
(vec (mapcat
#(list %1 (list fns %2))
(eval names)
(iterate inc 0)))
body))
(def mylocal-names ['A 'B])
(def mylocal-fns [(fn [] I'm A) (fn [] I'm B)])

so that
(letlocals mylocal-names mylocal-fns (str (A) (B))
macroexpand-1s into
(let [A (mylocal-fns 0) B (mylocal-fns 1)] (str (A) (B)))


(3) expand the fn form bodies literally into the local definitions. This
has three downsides: no closures, code bloat, potential for bugs if the
surrounding code defines locals that override vars that are used in the fns.


Right now, (2) looks like the most attractive option to me, although I
kind of preferred the original version's clarity. The macro can of
course be made cleaner than my quick-and-dirty idea above, for example
by utilising let's destructuring features.

If I've missed anything, and anyone has any other (better!) suggestions,
I'd like to hear them of course! :)

Thanks,
~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 [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/clojure?hl=en
-~--~~~~--~~--~--~---



Re: java interop question

2008-10-27 Thread Phil Jordan

notallama wrote:
 this may be more of a java question than a clojure question. i dunno.
 
 how do i use a java class from clojure?
 
 it's easy enough if it's one of the default java libraries, but so far
 all i have managed with classes i wrote is unable to resolve to
 classname
 
 i tried running the repl from the directory with the classes i want to
 use, but no luck.

You need to set up your classpath correctly. How you do that depends
heavily on how you're launching clojure in the first place. I suspect
it's a batch file or shell script, in which case you just need to append
the path to your class hierarchy (or the jar file containing it) to the
-cp parameter to the java command. On UNIX-like systems, classpath
entries are separated by a colon (:), on Windows, I think it's a
semicolon (;).

So if your class Foo, in package bar is at /tmp/bar/Foo.class, you want
to change the part of the java line from

java -cp path/clojure.jar:otherstuff

to

java -cp path/clojure.jar:otherstuff:/tmp/

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 [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/clojure?hl=en
-~--~~~~--~~--~--~---