Thank you for taking the time to provide feedback. > I can't think of a direct equivalent now, but it's straightforward > enough to supply your own equivalent, like my compound? function > (basically #(and (seq? %) (seq %))). This won't work on arrays and > other things which seq can operate upon, but which respond with a > false to seq?, though. If that could be a problem, you can just use > seq and for non-seqables catch the exception and return false.
intentionally driving into an exception seems like a very bad idea. but yes I can define my own equivalent of consp I was just hoping there was something I was over looking. > You could perform a CPS transform by hand (which I haven't (yet) done > with the code in the Gist). For occurs-check this would mean having an > extra to-do argument (or maybe just [... & to-do]); whenever false > would normally be returned, you'd first check if to-do is empty, > returning false if so and doing (recur <args taken from (first to-do)> > (rest to-do)) otherwise. true should short-circuit and disregard > to-do, of course. Finally, whenever occurs-check would normally branch > on first / rest, it would recur with first *and* an extended to-do > including rest. That'll trade some heap for stack space, presumably worthwhile. I haven't had to do much hand optimization of recursion so I'm a little rusty (the Franz Allegro compiler was fairly amazing in that regard)... I was hoping there was some even better trick (like the trampoline) to save me even more. That's a good idea though, I'll just to estimate how close I'm getting to blowing the stack, and the overhead of tracking to-do. > Not at all. next is basically #(seq (rest %)); you might want to use > it when the extra bit of strictness is beneficial to your code and you > shouldn't use it when it isn't. oh, ok. I saw some posts on using the new super-lazy lists / code and it implied that next was necessary over rest, but I didn't quite read it in excruciating detail. > Good question. I really prefer the fully parenthesised cond clauses > from Scheme / CL / elisp, in good part because of the nicer > indentation without particularly elaborate rules... :-( that "extra" paren is there too because the body of the clause is an implicit progn in common lisp. > I think that your code would benefit from direct use of Clojure data > structure literals and such facilities as the IFn implementation > available on maps; see my gist for examples of what I have in mind. on some issues I agree others I disagree, regarding your code: regarding removing the helper functions, that's not necessarily ideal, while bindings may always be a map putting in literals like {} or nil instead of fail or no-bindings actually obfuscates the code, and makes it harder to change later if what bindings is needs to fundamentally change. similarly with truncating variable names, it detracts from readability - although with one interesting caveat -- "var" is a particularly unique as it is now a language keyword in clojure. in general it's bad style to wrap a cond with another test if trivially avoidable (at least this is coming from best practice common lisp style) so for example, in your code line 24, there's no reason to pull that out of the cond, especially since you are also using the return value of the when, it's better to be explicit about the test and what it's resulting return value is -- in this case that the unification fails. the if-let's are a little more interesting as it potentially saves one lookup, but it seems to make the code far more "disjoint" or at least increase the number of paths. the changing of a cond to a some with the identity function on line 48 seems like a very odd choice to me. while still correct code I see no reason why this would be preferred to a cond. thanks for the subst code. I'm torn about the simplify-bindings -- I think it's a cool function to have, because if what you are subbing into is large compared to bindings, this is probably a win, but if you have a lot of bindings and are only using some, you could spend a lot of time to pack them down. it'd probably be good to have around if someone knew where they were in the trade-off. I left out Norvig's code, I have a need for binding/substituting with multiple binding lists so I ended up with this (which requires clojure.walk) (defn recursive-replace ([expr l-bindings] (recursive-replace expr l-bindings #{expr})) ([expr l-bindings past-vals] (let [applicable-bindings (some (fn [b] (if (contains? b expr) b nil)) l-bindings) bind-val (get applicable-bindings expr)] (if (or (not applicable-bindings) (contains? past-vals bind-val)) ;don't enter replace loop expr (recur bind-val l-bindings (conj past-vals bind-val)))))) (defn subst-bindings-int [x l-bindings] (prewalk (fn [expr] (recursive-replace expr l-bindings)) x)) (defn subst-bindings-list [x list-of-bindings] (cond (some (partial = fail) list-of-bindings) fail (every? (partial = no-bindings) list-of-bindings) x :else (subst-bindings-int x list-of-bindings))) (defn subst-bindings [x & bindings] "Substitute the value of variables in bindings into x, taking recursively bound variables into account." (subst-bindings-list x bindings)) thanks again for the new ideas. Kevin -- 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