Re: Cond, and abusing or
Hi, Am 16.01.2010 um 01:48 schrieb Scott Burson: Certainly, this is a very common idiom in Common Lisp and other older dialects. I guess there are a few people who don't like it, but a lot of us do it routinely. You'll even see stuff like (or (try-to-construct-a-foo) (error Couldn't construct a foo)) Just be aware that if a nil or false is a valid answer this approach will fail. Sincerely Meikel -- 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
Cond, and abusing or
There's an old programmers hack that works in many languages of abusing the logical or operator to try a sequence of operations until one returns something useful. It appears that this works in Clojure, too. The reason I tried it is that I'm unhappy with Clojure's implementation of cond. Consider the following interaction: You are in a shed. There is a box here There is a knife here = open the box You open the box. There is a knife in the box = Take the knife out of the box You take the knife out of the box Now, consider a recursive repertoire structure in which each element is a tuple {token, before-action, sub-repertoire, after-action} a subset of which is like this ((take () ((the (bind-target-and-continue player (cdr line) (caddr repertoire) context) ((out () () (take-from-container player (cdr line) (caddr repertoire) context)) )) (take-from-environment player (cdr line) (caddr repertoire) context))) The action parts may internally recurse into the interpreter; so for example bind-target-and-continue puts the token that follows the into the context as the target and then re-enters the interpreter with the remainder of the line and the sub-repertoire So given 'take the knife out of the box' == at take there is no before action, so we interpret the sub- repertoire at the there is a before action, so we bind knife as target, and interpret the rest of the line with the sub-repertoire == at out there is no before-action or sub-repertoire, so we evaluate the after-action which tells us to treat what's left of the line (of the box) as a container specifier, locate a container that matches that description in the environment, and take the knife out of it, rather than the knife in the environment But given just 'take the knife' == at take there is no before action, so we interpret the sub- repertoire at the there is a before action, so we bind knife as target, and interpret the rest of the line with the sub-repertoire but there is no rest-of-line, so interpreting it returns null == so we interpret the post action on take, and take the knife from the environment, not the one in the box. You can build up remarkably convincing interpreters for a subset of imperative English with this and a few Eliza style tricks. In portable standard lisp one would write (cond ((eval before-action)) ((interpret player sub-repertoire context)) ((eval after-action))) This is because each clause of the cond statement was an implicit do list, so (let ((a 'foo)(b nil)) (cond (a) (b))) = foo Each of a and b are evaluated at most once (as a has a non-nil binding in this case b is not evaluated at all). To achieve this with Clojure's cond I have to do either (let [a 'foo b nil] (cond a a b b)) = foo in which case a is evaluated twice; or (let [a 'foo b nil] (let [value-of-a a value-of-b b] (cond (not (= nil value-of-a)) value-of-a (not (= nil value-of-b)) value-of-b))) = foo which only evaluates a once but it always evaluates b whether it needs to or not, /and/ it's fugly and hard to read, /and/ it's very likely inefficient (but someone more fluent in Clojure could probably replace my (not (= nil thing)) with something cleaner); or else (let [a 'foo b nil] (or a b)) = foo which is simple and easy to read, but is exploiting the left-to-right evaluation strategy of the or operator. It isn't (or ought not to need to be) part of the contract of the or operator that it evaluates left to right. On a massively parallel machine, all branches of the or might be explored simultaneously. So this sort of hack leaves me feeling dirty. Comments? Is there (once again) something more elegant I've missed? -- 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: Cond, and abusing or
On Jan 15, 5:02 pm, Simon Brooke still...@googlemail.com wrote: There's an old programmers hack that works in many languages of abusing the logical or operator to try a sequence of operations until one returns something useful. It appears that this works in Clojure, too. The reason I tried it is that I'm unhappy with Clojure's implementation of cond. Consider the following interaction: You are in a shed. There is a box here There is a knife here = open the box You open the box. There is a knife in the box = Take the knife out of the box You take the knife out of the box Now, consider a recursive repertoire structure in which each element is a tuple {token, before-action, sub-repertoire, after-action} a subset of which is like this ((take () ((the (bind-target-and-continue player (cdr line) (caddr repertoire) context) ((out () () (take-from-container player (cdr line) (caddr repertoire) context)) )) (take-from-environment player (cdr line) (caddr repertoire) context))) The action parts may internally recurse into the interpreter; so for example bind-target-and-continue puts the token that follows the into the context as the target and then re-enters the interpreter with the remainder of the line and the sub-repertoire So given 'take the knife out of the box' == at take there is no before action, so we interpret the sub- repertoire at the there is a before action, so we bind knife as target, and interpret the rest of the line with the sub-repertoire == at out there is no before-action or sub-repertoire, so we evaluate the after-action which tells us to treat what's left of the line (of the box) as a container specifier, locate a container that matches that description in the environment, and take the knife out of it, rather than the knife in the environment But given just 'take the knife' == at take there is no before action, so we interpret the sub- repertoire at the there is a before action, so we bind knife as target, and interpret the rest of the line with the sub-repertoire but there is no rest-of-line, so interpreting it returns null == so we interpret the post action on take, and take the knife from the environment, not the one in the box. You can build up remarkably convincing interpreters for a subset of imperative English with this and a few Eliza style tricks. In portable standard lisp one would write (cond ((eval before-action)) ((interpret player sub-repertoire context)) ((eval after-action))) This is because each clause of the cond statement was an implicit do list, so (let ((a 'foo)(b nil)) (cond (a) (b))) = foo Each of a and b are evaluated at most once (as a has a non-nil binding in this case b is not evaluated at all). To achieve this with Clojure's cond I have to do either (let [a 'foo b nil] (cond a a b b)) = foo in which case a is evaluated twice; or (let [a 'foo b nil] (let [value-of-a a value-of-b b] (cond (not (= nil value-of-a)) value-of-a (not (= nil value-of-b)) value-of-b))) = foo which only evaluates a once but it always evaluates b whether it needs to or not, /and/ it's fugly and hard to read, /and/ it's very likely inefficient (but someone more fluent in Clojure could probably replace my (not (= nil thing)) with something cleaner); or else (let [a 'foo b nil] (or a b)) = foo which is simple and easy to read, but is exploiting the left-to-right evaluation strategy of the or operator. It isn't (or ought not to need to be) part of the contract of the or operator that it evaluates left to right. On a massively parallel machine, all branches of the or might be explored simultaneously. So this sort of hack leaves me feeling dirty. Comments? Is there (once again) something more elegant I've missed? or has left-to-right and short-circuiting in its contract and will never be executed in parallel (any parallel or would be a different thing). The 'hack' is fine, and clean. As far as cond, as in many areas, like 'let', Clojure opts out of the increased verbosity and clutter of the additional grouping, admittedly losing the facility you desire. It's a tradeoff I think serves the common case well. Note that condp supports a ternary clause form that shovels the test expr into the result expr (this more of a hack for your purposes): (let [a 'foo b nil] (condp #(or %1 %2) nil a : identity b : identity)) = foo There has been some talk of adding : to cond also. Rich -- 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
Re: Cond, and abusing or
On Jan 15, 2:02 pm, Simon Brooke still...@googlemail.com wrote: There's an old programmers hack that works in many languages of abusing the logical or operator to try a sequence of operations until one returns something useful. It appears that this works in Clojure, too. Certainly, this is a very common idiom in Common Lisp and other older dialects. I guess there are a few people who don't like it, but a lot of us do it routinely. You'll even see stuff like (or (try-to-construct-a-foo) (error Couldn't construct a foo)) -- Scott -- 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