Re: Changing State Pattern - Better Options?
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
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
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
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 -~--~~~~--~~--~--~---