Re: Looking for advice to fine tune a simple function
it's more idiomatic to use *when* rather than *if* for cases where you won't be considering the false path. (when (= x y) z) cond is used more as a multi-if with a drop out at the end (usually using :else which because it's a keyword is truthy when evaluated). On Wednesday, 19 February 2014 02:25:05 UTC, Laurent Droin wrote: Now that I have a better understanding of what some does (i didn't interpret the doc properly), it does totally make sense that it would be recursive, so that's great. While reducing my code with Johanna's feedback, I noticed I kept using cond and not if. Is there any meaningful difference between (if (= x y) z) and (cond (= x y) z) ? I see that if is a special form and cone is macro. I haven't reached the chapter about macros yet in the book I'm reading (programming Clojure). Not sure if it's relevant. -- 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 unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.
Re: Looking for advice to fine tune a simple function
Hi, Am Sonntag, 16. Februar 2014 23:31:46 UTC+1 schrieb Laurent Droin: Here is a function that I came up with that takes a bpm (heart beats per minute) value, as well as a sequence of 4 values that represent the boundaries defining the 5 different heart rate zones for a particular person. The function needs to finds out in what heart zone the bpm value falls into and return that zone (as a keyword - I later use that keyword in a map). I'd go with something like this: 1. (defn r [x ps] 2. (some (fn [[p e]] 3. (when (= x p) e)) 4. (reverse ps))) 5. 6. (r 7 [[0 :a] [5 :b] [10 :c]]) ;; = :b -billy -- 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 unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.
Re: Looking for advice to fine tune a simple function
You can use the *pad* argument in *partition* to add x as the value of :zone-5, like *(partition 2 2 [x] params)*. (Note you have to supply *step* if you supply *pad*.) Then you don't need the *or* or the *(last params)*. Also, like Billy did, you can use *some* instead of (*first (filter (complement nil?)...* The confusing thing about *some* is that it doesn't return the first value for which the predicate is true, it returns the value of the predicate the first time it is truthy. So using it to return the first one that isn't nil works perfectly. And the partial function doesn't really help you here because you already have access to x where you need to call your function. If you make those 3 changes, you'll have: (defn quantize [x params] (some (fn [[b c]] (if (= x c) b)) (partition 2 2 [x] params))) On Monday, February 17, 2014 9:04:22 PM UTC-8, Laurent Droin wrote: Hmmm, looks like I can replace #(not (nil? %)) by (complement nil?) which seems more elegant. Also, it looks like I don't need that (into [] ), which will keep the code cleaner. I think I could also get rid of the (or) by always adding (last params) at the end of the sequence from which I pick the first non null element. The problem with this is that I need to make sure I add this element at the end of the collection, and map returns a list, which means that I would need to convert it into a vector if I want to use conj. Or maybe there's a way to guarantee that I can add an element at the end of the list. I know it's not cheap but I'm not sure if it will make a significant difference for what I'm doing. I think I could really abuse the into function and that doesn't seem quite right. -- 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 unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.
Re: Looking for advice to fine tune a simple function
Wow, that's amazing. Thanks Billy and Johanna. I'm going to try all this tonight. What I'm not sure of (I don't have a good understanding yet about lazy sequences) is whether or not the sequence given to some is lazy or not. For example, if I have thousands of parameters but x is = the first boundary value (i.e. the first element in the sequence), I don't really want to test x against all the other boundary values in the sequence since I already know it's not nil. Otherwise, I was thinking I could use recursion, take the two first elements of the sequence, and see if I could conclude. If not, I would call the function again against the rest of the sequence. -- 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 unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.
Re: Looking for advice to fine tune a simple function
I know. Isn't Clojure beautiful? Sigh... In the docs for *partition*http://clojuredocs.org/clojure_core/clojure.core/partition, it says that partition returns a lazy sequence. And if you look at the source section of the docs for *some*http://clojuredocs.org/clojure_core/clojure.core/some you can see that it uses recursion under the hood. So you should be covered there. Thanks for the interesting discussion. =) On Tuesday, February 18, 2014 11:56:17 AM UTC-8, Laurent Droin wrote: Wow, that's amazing. Thanks Billy and Johanna. I'm going to try all this tonight. What I'm not sure of (I don't have a good understanding yet about lazy sequences) is whether or not the sequence given to some is lazy or not. For example, if I have thousands of parameters but x is = the first boundary value (i.e. the first element in the sequence), I don't really want to test x against all the other boundary values in the sequence since I already know it's not nil. Otherwise, I was thinking I could use recursion, take the two first elements of the sequence, and see if I could conclude. If not, I would call the function again against the rest of the sequence. -- 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 unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.
Re: Looking for advice to fine tune a simple function
He he... more research work for me tonight. Thanks Johanna for the pointers. -- 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 unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.
Re: Looking for advice to fine tune a simple function
Now that I have a better understanding of what some does (i didn't interpret the doc properly), it does totally make sense that it would be recursive, so that's great. While reducing my code with Johanna's feedback, I noticed I kept using cond and not if. Is there any meaningful difference between (if (= x y) z) and (cond (= x y) z) ? I see that if is a special form and cone is macro. I haven't reached the chapter about macros yet in the book I'm reading (programming Clojure). Not sure if it's relevant. -- 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 unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.
Re: Looking for advice to fine tune a simple function
On Sunday, February 16, 2014 11:19:58 PM UTC-5, Bruno Kim Medeiros Cesar wrote: (BTW, Andy, how do you format your code so prettily?) I usually just paste it into pygments.org and then paste it back into google groups (which accepts the generated HTML). Cheers -- 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 unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.
Re: Looking for advice to fine tune a simple function
Wow, thanks Gianluco, Andy and Bruno. Lots of good feedback I am trying to process. It's amazing how coming up with a satisfying functional programing style function is a complex process when you've been doing imperative programing all your life. It's really a whole different way of thinking. I like it but it will take some practice and your feedback really helps. It looks like I'm going to spend more time on it. I'll update. -- 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 unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.
Re: Looking for advice to fine tune a simple function
Thanks for this! On Monday, February 17, 2014 4:49:38 AM UTC-8, Andy- wrote: On Sunday, February 16, 2014 11:19:58 PM UTC-5, Bruno Kim Medeiros Cesar wrote: (BTW, Andy, how do you format your code so prettily?) I usually just paste it into pygments.org and then paste it back into google groups (which accepts the generated HTML). Cheers -- 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 unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.
Re: Looking for advice to fine tune a simple function
Hi, I'm fairly new to clojure as well, but I like this way, and I *think* it's idiomatic: Data structure: [{:zone-max 100 :zone-key :hr-zone-1} {:zone-max 120 :zone-key :hr-zone-2} {:zone-max 140 :zone-key :hr-zone-3} {:zone-max 160 :zone-key :hr-zone-4} {:zone-max 333 :zone-key :hr-zone-5}] And function: (defn hr-zone Return the HR zone as a keyword according to the bpm value[bpm zones] (:zone-key (first (filter #(= bpm (:zone-max %)) (sort-by :zone-max zones) That's just over my parentheses-parsing comfort level, so I'd probably thread it for readability: (defn hr-zone Return the HR zone as a keyword according to the bpm value[bpm zones] (- zones (sort-by :zone-max) (filter #(= bpm (:zone-max %))) (first) (:zone-key))) This returns nil if given a bpm higher than the defined zones, and it considers negative heart rates to be in zone one, so you might want to handle the boundaries differently. Happy coding, Johanna On Monday, February 17, 2014 6:00:40 AM UTC-8, Laurent Droin wrote: Wow, thanks Gianluco, Andy and Bruno. Lots of good feedback I am trying to process. It's amazing how coming up with a satisfying functional programing style function is a complex process when you've been doing imperative programing all your life. It's really a whole different way of thinking. I like it but it will take some practice and your feedback really helps. It looks like I'm going to spend more time on it. I'll update. -- 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 unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.
Re: Looking for advice to fine tune a simple function
All the good feedback here had me thinking a lot... Especially Andy who pushed me towards more abstraction. I loved the idea of functions that return functions and researching all this led me to embrace partials. Here is my current implementation of quantize. It came out of a lot of trial and errors (gotta love the REPL) and even though, it became more concise, - I think it can be improved - I am wondering if I'm going too far. I'm worried that it makes the function hard to understand for someone who would maintain it. I don't have enough FP experience to get an idea whether this function is well written, or if it's just impossible to understand. From an imperative programming background, it is certainly impossible to understand. (def inf Long/MAX_VALUE) (defn quantize Given a collection of ordered numeric 'markers' such as [m n], given a collection of values such as [a b c], returns a if x = m, returns b if m x = n, returns c if n x This should work for any size collections, but the values neeed to have one more element than the markers. [x markers values] (let [f (partial (fn([%1 %2 %3] (cond (= %1 %2) %3))) x)] (first (filter #(not (nil? %))(map f (conj markers inf) values) A few things I don't like: - (conj markers inf) to artificially add an infinite enough value to my markers collection so that it turns [120 150 165 180] into [120 150 165 180 infinity]. Too bad there is no infinity in Clojure. - I hate the #(not (nil? %)) but I could not find a predicate such as not-nil? - I was not able to get (some) to work in this context so I used (first (filter instead. - I could probably make this more readable by applying the Clojure styling guide (I will) Any idea how I could improve this function further for more elegance and clarity? With this function working, I can stick it to some foundation library and create a simpler hr library. My hr library would contain: (def hr-zones [:z1 :z2 :z3 :z4 :z5]) (defn get-hr-zone [hr-zones-def bpm] (quantize bpm hr-zones-def hr-zones)) And then, my client application would have: (def get-my-hr-zone (partial get-hr-zone [120 150 165 180])) (get-my-hr-zone 119)(get-my-hr-zone 120)(get-my-hr-zone 149)(get-my-hr-zone 150)(get-my-hr-zone 151) BTW, thanks Andy for the tip about Pygments. -- 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 unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.
Re: Looking for advice to fine tune a simple function
On Monday, February 17, 2014 6:43:18 PM UTC-5, Laurent Droin wrote: (def inf Long/MAX_VALUE) (defn quantize [x markers values] (let [f (partial (fn([%1 %2 %3] (cond (= %1 %2) %3))) x)] (first (filter #(not (nil? %))(map f (conj markers inf) values) The reason I didn't go with Long/MAX_VALUE is that it breaks down for arbitrary precision and bigints which is a no-no for me. There is many many ways this function could look. Mine is just one example. For instance many clojurist's would probably prefer to call it like this: (quantize 0 :small 5 :med :10 :large :huge). I didn't go through your code. But many people don't like to overuse anonymous functions especially if you use 3 parameters (what is %1, %2 %3?, if they had a name it'd be clearer). But this is just style so it's very subjective. Cheers -- 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 unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.
Re: Looking for advice to fine tune a simple function
All good points Andy. Thanks. I'll continue working on it until I'm happy. I am almost never happy with anonymous functions either. In this particular case, though, the function is so simple that I felt it would be overkill to externalize it into another function (unless I'm really going to use it somewhere else). The fact that we don't know what the parameters are didn't bother me too much: (cond (= %1 %2) %3)) I just wanted something that return the third parameter if parameter 1 = parameter 2. It seemed too abstract to try to label the parameters. About the Long/MAX_VALUE, I didn't like it and you're right that it will only work for integers (if I read correctly, they are actually long in Clojure) but that won't work for all other numerical types. I don't see any solution to my problem besides having to do a test that is different from all the other tests in the function. I also like the function signature you came up with initially. It does make sense and it's probably more elegant than passing two collections. But as you said, there are many ways to implement the same thing. I would like to follow best practices as much as possible. -- 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 unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.
Re: Looking for advice to fine tune a simple function
So here is a new attempt at the quantize function so that I no longer have to deal with the infinity problem: (defn quantize-2 [x params] (let [f (partial (fn [a [b c]] (cond(= a c) b)) x)] (or (first (filter #(not (nil? %)) (map f (into [](partition 2 2 params))) (last params)) params now has this form: [:zone-1 120 :zone-2 150 :zone-3 165 :zone-4 180 :zone-5] i.e. the values to return are intermingled with the boundaries. I normally don't like mixing things but you've gotta admit that it looks just like how you would draw heart rates on a schema on paper: zone1 | zone 2 | zone 3 | zone 4 | zone 5 and you would probably have the values 120, 150, 165 and 180 written in between the zone names, just above the | characters. The way I got rid of the infinity is by using the or. I'm looking for the first non null value (when trying to find whether x is = 120, = 150, =165, =180, and if I don't find any, then it must be that we're in the last category of my params collection. Still not sure whether it's the optimum way to do it but I'll continue researching (after all, this is not a professional project, I have no deadline, and I can keep trying to improve - great way to learn). The feedback I got on this post got me working and scratching my head all weekend. This is great. -- 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 unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.
Re: Looking for advice to fine tune a simple function
So here is a new attempt at the quantize function so that I no longer have to deal with the infinity problem: (defn quantize-2 [x params] (let [f (partial (fn [a [b c]] (cond(= a c) b)) x)] (println (first (filter #(not (nil? %))(map f (into [](partition 2 params)) (or (first (filter #(not (nil? %)) (map f (into [](partition 2 params) (last params params now has this form: [:zone-1 120 :zone-2 150 :zone-3 165 :zone-4 180 :zone-5] i.e. the values to return are intermingled with the boundaries. I normally don't like mixing things but you've gotta admit that it looks just like how you would draw heart rates on a schema on paper: zone1 | zone 2 | zone 3 | zone 4 | zone 5 and you would probably have the values 120, 150, 165 and 180 written in between the zone names, just above the | characters. The way I got rid of the infinity is by using the or. I'm looking for the first non null value (when trying to find whether x is = 120, = 150, =165, =180, and if I don't find any, then it must be that we're in the last category of my params collection. Still not sure whether it's the optimum way to do it but I'll continue researching (after all, this is not a professional project, I have no deadline, and I can keep trying to improve - great way to learn). The feedback I got on this post got me working and scratching my head all weekend. This is great. -- 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 unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.
Re: Looking for advice to fine tune a simple function
So here is a new attempt at the quantize function so that I no longer have to deal with the infinity problem: (defn quantize-2 [x params] (let [f (partial (fn [a [b c]] (cond(= a c) b)) x)] (or (first (filter #(not (nil? %)) (map f (into [](partition 2 params) (last params params now has this form: [:zone-1 120 :zone-2 150 :zone-3 165 :zone-4 180 :zone-5] i.e. the values to return are intermingled with the boundaries. I normally don't like mixing things but you've gotta admit that it looks just like how you would draw heart rates on a schema on paper: zone1 | zone 2 | zone 3 | zone 4 | zone 5 and you would probably have the values 120, 150, 165 and 180 written in between the zone names, just above the | characters. The way I got rid of the infinity is by using the or. I'm looking for the first non null value (when trying to find whether x is = 120, = 150, =165, =180, and if I don't find any, then it must be that we're in the last category of my params collection. Still not sure whether it's the optimum way to do it but I'll continue researching (after all, this is not a professional project, I have no deadline, and I can keep trying to improve - great way to learn). The feedback I got on this post got me working and scratching my head all weekend. This is great. -- 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 unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.
Re: Looking for advice to fine tune a simple function
Hmmm, looks like I can replace #(not (nil? %)) by (complement nil?) which seems more elegant. Also, it looks like I don't need that (into [] ), which will keep the code cleaner. I think I could also get rid of the (or) by always adding (last params) at the end of the sequence from which I pick the first non null element. The problem with this is that I need to make sure I add this element at the end of the collection, and map returns a list, which means that I would need to convert it into a vector if I want to use conj. Or maybe there's a way to guarantee that I can add an element at the end of the list. I know it's not cheap but I'm not sure if it will make a significant difference for what I'm doing. I think I could really abuse the into function and that doesn't seem quite right. -- 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 unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.
Looking for advice to fine tune a simple function
Hi, Disclaimer - I am completely new to Clojure. I just implemented my very first (simple) program, letting me find out, from a GPX file, how much time is spent in the various heart rate zones. Now that it's working, I'm reviewing the code and trying to use best practices. From what I have read so far, there are many ways in Clojure to do the same thing and for a newbie, it's not always obvious to get a good grasp on what is the best way to code a feature. As a developer, and even though I love how concise Clojure programs can be, I am very concerned with readability and ease of maintenance so I would like to keep functions as short and tight as possible, but not to the point where it becomes hard to understand what it does. Here is a function that I came up with that takes a bpm (heart beats per minute) value, as well as a sequence of 4 values that represent the boundaries defining the 5 different heart rate zones for a particular person. The function needs to finds out in what heart zone the bpm value falls into and return that zone (as a keyword - I later use that keyword in a map). If you're an experienced Clojure developer, what would you have done differently (and why) ? *(defn hr-zone* * Return the HR zone as a keyword according to the bpm value.* * [bpm [max-zone-1 max-zone-2 max-zone-3 max-zone-4]] * * (cond * *(= bpm max-zone-1) :hr-zone-1* *(and ( max-zone-1 bpm) (= max-zone-2 bpm)) :hr-zone-2 * *(and ( max-zone-2 bpm) (= max-zone-3 bpm)) :hr-zone-3 * *(and ( max-zone-3 bpm) (= max-zone-4 bpm)) :hr-zone-4 * *( max-zone-4 bpm) :hr-zone-5))* FYI, here is how I call this function in the REPL: (def my-hr-zones-defs [120 150 165 180]) (hr-zone 115 my-hr-zones-defs) (hr-zone 133 my-hr-zones-defs) (hr-zone 161 my-hr-zones-defs) (hr-zone 175 my-hr-zones-defs) (hr-zone 192 my-hr-zones-refs) Questions I have: Would that make sense to consider (and maybe enforce) that the sequence received as parameter is sorted? If this was the case, I would assume I could avoid the and calls- assuming that all the conditions in the cone form are evaluated in order. Assuming that I need to test bpm against the two boundaries of each range, would there be a better way to do this: *(and ( max-zone-1 bpm) (= max-zone-2 bpm))* ? Maybe this would work, but is it preferable? *( max-zone-1 bpm (dec **max-zone-2**))* *Thanks in advance for the advice.* *Laurent. * -- 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 unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.
Re: Looking for advice to fine tune a simple function
to me it seems that you are anyway relying on the assumption that the sequence is ordered, so I think it would be convenient to drop the ands Gianluca On Sunday, February 16, 2014 11:31:46 PM UTC+1, Laurent Droin wrote: Hi, Disclaimer - I am completely new to Clojure. I just implemented my very first (simple) program, letting me find out, from a GPX file, how much time is spent in the various heart rate zones. Now that it's working, I'm reviewing the code and trying to use best practices. From what I have read so far, there are many ways in Clojure to do the same thing and for a newbie, it's not always obvious to get a good grasp on what is the best way to code a feature. As a developer, and even though I love how concise Clojure programs can be, I am very concerned with readability and ease of maintenance so I would like to keep functions as short and tight as possible, but not to the point where it becomes hard to understand what it does. Here is a function that I came up with that takes a bpm (heart beats per minute) value, as well as a sequence of 4 values that represent the boundaries defining the 5 different heart rate zones for a particular person. The function needs to finds out in what heart zone the bpm value falls into and return that zone (as a keyword - I later use that keyword in a map). If you're an experienced Clojure developer, what would you have done differently (and why) ? *(defn hr-zone* * Return the HR zone as a keyword according to the bpm value.* * [bpm [max-zone-1 max-zone-2 max-zone-3 max-zone-4]] * * (cond * *(= bpm max-zone-1) :hr-zone-1* *(and ( max-zone-1 bpm) (= max-zone-2 bpm)) :hr-zone-2 * *(and ( max-zone-2 bpm) (= max-zone-3 bpm)) :hr-zone-3 * *(and ( max-zone-3 bpm) (= max-zone-4 bpm)) :hr-zone-4 * *( max-zone-4 bpm) :hr-zone-5))* FYI, here is how I call this function in the REPL: (def my-hr-zones-defs [120 150 165 180]) (hr-zone 115 my-hr-zones-defs) (hr-zone 133 my-hr-zones-defs) (hr-zone 161 my-hr-zones-defs) (hr-zone 175 my-hr-zones-defs) (hr-zone 192 my-hr-zones-refs) Questions I have: Would that make sense to consider (and maybe enforce) that the sequence received as parameter is sorted? If this was the case, I would assume I could avoid the and calls- assuming that all the conditions in the cone form are evaluated in order. Assuming that I need to test bpm against the two boundaries of each range, would there be a better way to do this: *(and ( max-zone-1 bpm) (= max-zone-2 bpm))* ? Maybe this would work, but is it preferable? *( max-zone-1 bpm (dec **max-zone-2**))* *Thanks in advance for the advice.* *Laurent. * -- 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 unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.
Re: Looking for advice to fine tune a simple function
I'm also very new to clojure but this is how I'd do it: (def hr-zones { [0 100] :low [101 120] :fat-burn [121 140] :aerobic [141 160] :anaerobic [161 333] :max}) (defn hr-zone [hr] (some (fn [x] (and (= (first (key x)) hr (second (key x))) (val x))) hr-zones)) This method does have some disadvantages however (you have to make sure the boundaries are correct and it doesn't handle floating points like 140.4). Whenever you see yourself repeating code (like the 3-4 if's you have) then you have to take a step back and maybe change your data structure so that your algorithm is easier. Mathematically speaking you're really quantizing your data (aka applying a piecewise constant function). So you could even take another step back and make it more general. So find a data structure that represents the function (ie the intervals and the function value for each interval) and then you could come up with a function like quantize or mk-piecewise-const-fn that returns a function. So you could then do: (let [myfn (mk-piecewise-const-fn [[100 :zone-1] [120 :zone-2] ...] ] myzone (myfn 142)] and you have a general function which you can now use with a much wider range of problems (not just specific to your hr-zone conversion). But I'm too lazy to come up with a implementation for mk-piecewise-const-fn :) HTH On Sunday, February 16, 2014 5:31:46 PM UTC-5, Laurent Droin wrote: Hi, Disclaimer - I am completely new to Clojure. I just implemented my very first (simple) program, letting me find out, from a GPX file, how much time is spent in the various heart rate zones. Now that it's working, I'm reviewing the code and trying to use best practices. From what I have read so far, there are many ways in Clojure to do the same thing and for a newbie, it's not always obvious to get a good grasp on what is the best way to code a feature. As a developer, and even though I love how concise Clojure programs can be, I am very concerned with readability and ease of maintenance so I would like to keep functions as short and tight as possible, but not to the point where it becomes hard to understand what it does. Here is a function that I came up with that takes a bpm (heart beats per minute) value, as well as a sequence of 4 values that represent the boundaries defining the 5 different heart rate zones for a particular person. The function needs to finds out in what heart zone the bpm value falls into and return that zone (as a keyword - I later use that keyword in a map). If you're an experienced Clojure developer, what would you have done differently (and why) ? *(defn hr-zone* * Return the HR zone as a keyword according to the bpm value.* * [bpm [max-zone-1 max-zone-2 max-zone-3 max-zone-4]] * * (cond * *(= bpm max-zone-1) :hr-zone-1* *(and ( max-zone-1 bpm) (= max-zone-2 bpm)) :hr-zone-2 * *(and ( max-zone-2 bpm) (= max-zone-3 bpm)) :hr-zone-3 * *(and ( max-zone-3 bpm) (= max-zone-4 bpm)) :hr-zone-4 * *( max-zone-4 bpm) :hr-zone-5))* FYI, here is how I call this function in the REPL: (def my-hr-zones-defs [120 150 165 180]) (hr-zone 115 my-hr-zones-defs) (hr-zone 133 my-hr-zones-defs) (hr-zone 161 my-hr-zones-defs) (hr-zone 175 my-hr-zones-defs) (hr-zone 192 my-hr-zones-refs) Questions I have: Would that make sense to consider (and maybe enforce) that the sequence received as parameter is sorted? If this was the case, I would assume I could avoid the and calls- assuming that all the conditions in the cone form are evaluated in order. Assuming that I need to test bpm against the two boundaries of each range, would there be a better way to do this: *(and ( max-zone-1 bpm) (= max-zone-2 bpm))* ? Maybe this would work, but is it preferable? *( max-zone-1 bpm (dec **max-zone-2**))* *Thanks in advance for the advice.* *Laurent. * -- 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 unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.
Re: Looking for advice to fine tune a simple function
I realized I could use that at some of my code so I wrote it. Not sure if it's the best possible implementation but here it is: (defn quantizer Returns a function that quantizes input data which when called with 'x' returns: o 1st val if-Inf x = 1st bound o 2st val if 1st bound x = 2st bound o ... o last val if last-1 bound x = last bound o max if x last bound where m is a vector of vectors where the first element specifies the boundary and the second element the value which to return. Example: (def points-grade (quantizer [[40 :F] [60 :D] [80 :C] [90 :B]] :A)) (map points-grade [10 80 93]) ; (:F :C :A) [m max] (fn [x] (if (= x (first (first m))) (second (first m)) (if ( x (first (last m))) max (some (fn [i] (let [[[l lv] [h hv]] i] (and ( l x) (= x h) hv))) (partition 2 1 m)) Cheers On Sunday, February 16, 2014 6:47:51 PM UTC-5, Andy- wrote: I'm also very new to clojure but this is how I'd do it: (def hr-zones { [0 100] :low [101 120] :fat-burn [121 140] :aerobic [141 160] :anaerobic [161 333] :max}) (defn hr-zone [hr] (some (fn [x] (and (= (first (key x)) hr (second (key x))) (val x))) hr-zones)) This method does have some disadvantages however (you have to make sure the boundaries are correct and it doesn't handle floating points like 140.4). Whenever you see yourself repeating code (like the 3-4 if's you have) then you have to take a step back and maybe change your data structure so that your algorithm is easier. Mathematically speaking you're really quantizing your data (aka applying a piecewise constant function). So you could even take another step back and make it more general. So find a data structure that represents the function (ie the intervals and the function value for each interval) and then you could come up with a function like quantize or mk-piecewise-const-fn that returns a function. So you could then do: (let [myfn (mk-piecewise-const-fn [[100 :zone-1] [120 :zone-2] ...] ] myzone (myfn 142)] and you have a general function which you can now use with a much wider range of problems (not just specific to your hr-zone conversion). But I'm too lazy to come up with a implementation for mk-piecewise-const-fn :) HTH On Sunday, February 16, 2014 5:31:46 PM UTC-5, Laurent Droin wrote: Hi, Disclaimer - I am completely new to Clojure. I just implemented my very first (simple) program, letting me find out, from a GPX file, how much time is spent in the various heart rate zones. Now that it's working, I'm reviewing the code and trying to use best practices. From what I have read so far, there are many ways in Clojure to do the same thing and for a newbie, it's not always obvious to get a good grasp on what is the best way to code a feature. As a developer, and even though I love how concise Clojure programs can be, I am very concerned with readability and ease of maintenance so I would like to keep functions as short and tight as possible, but not to the point where it becomes hard to understand what it does. Here is a function that I came up with that takes a bpm (heart beats per minute) value, as well as a sequence of 4 values that represent the boundaries defining the 5 different heart rate zones for a particular person. The function needs to finds out in what heart zone the bpm value falls into and return that zone (as a keyword - I later use that keyword in a map). If you're an experienced Clojure developer, what would you have done differently (and why) ? *(defn hr-zone* * Return the HR zone as a keyword according to the bpm value.* * [bpm [max-zone-1 max-zone-2 max-zone-3 max-zone-4]] * * (cond * *(= bpm max-zone-1) :hr-zone-1* *(and ( max-zone-1 bpm) (= max-zone-2 bpm)) :hr-zone-2 * *(and ( max-zone-2 bpm) (= max-zone-3 bpm)) :hr-zone-3 * *(and ( max-zone-3 bpm) (= max-zone-4 bpm)) :hr-zone-4 * *( max-zone-4 bpm) :hr-zone-5))* FYI, here is how I call this function in the REPL: (def my-hr-zones-defs [120 150 165 180]) (hr-zone 115 my-hr-zones-defs) (hr-zone 133 my-hr-zones-defs) (hr-zone 161 my-hr-zones-defs) (hr-zone 175 my-hr-zones-defs) (hr-zone 192 my-hr-zones-refs) Questions I have: Would that make sense to consider (and maybe enforce) that the sequence received as parameter is sorted? If this was the case, I would assume I could avoid the and calls- assuming that all the conditions in the cone form are evaluated in order. Assuming that I need to test bpm against the two boundaries of each range, would there be a better way to do this: *(and ( max-zone-1 bpm) (= max-zone-2 bpm))* ? Maybe this would work, but is it preferable? *( max-zone-1 bpm (dec **max-zone-2**))* *Thanks in advance for the advice.* *Laurent. * -- You received this message because
Re: Looking for advice to fine tune a simple function
I can't claim to be an experienced Clojure developer, specially so regarding maintainability as I'm the only reader of what I write. Andy provided a great piece of code, although I scratched my head for a second or two unwrapping the last two lines. You'll be surprised how often (partition * 1) comes up, as it provides a sliding window through a sequence. (BTW, Andy, how do you format your code so prettily?) Regarding your question about expecting a sorted input: why don't you ensure it? These are the (very similar) options to enforce a precondition that I know of: *(defn hr-zone [bpm [zone-1 zone-2 zone-3 zone-4 :as zones]] {:pre [( zone-1 zone-2 zone-3 zone-4)]} ...) ; I don't see this very often, but it's simple(defn hr-zone [bpm [zone-1 zone-2 zone-3 zone-4 :as zones]] (assert ( zone-1 zone-2 zone-3 zone-4)} Not sorted!) ...) ; throws an error just as the above one, but with a provided message(defn hr-zone [bpm [zone-1 zone-2 zone-3 zone-4 :as zones]] (when-not ( zone-1 zone-2 zone-3 zone-4)} (throw (Exception. Not sorted!))) ...) ; If you want to be able to catch the error, you may throw an exception*sorted? does not do what you may expect: it tests whether the data structure implements Sorted, such as sorted-set and sorted-map. You may get the same behaviour with (apply = coll) or (apply coll) for numeric collections. For me, the simplest option is *(defn hr-zone [bpm zones] (let [[zone-1 zone-2 zone-3 zone-4] (sort zones)]...)* which never fails, although may not be what you need (for example, you'd like to be alerted if you input [174 178 128 186] when you wanted [174 178 182 186]). Hope that helps a bit, Bruno Kim. On Sunday, February 16, 2014 7:31:46 PM UTC-3, Laurent Droin wrote: Hi, Disclaimer - I am completely new to Clojure. I just implemented my very first (simple) program, letting me find out, from a GPX file, how much time is spent in the various heart rate zones. Now that it's working, I'm reviewing the code and trying to use best practices. From what I have read so far, there are many ways in Clojure to do the same thing and for a newbie, it's not always obvious to get a good grasp on what is the best way to code a feature. As a developer, and even though I love how concise Clojure programs can be, I am very concerned with readability and ease of maintenance so I would like to keep functions as short and tight as possible, but not to the point where it becomes hard to understand what it does. Here is a function that I came up with that takes a bpm (heart beats per minute) value, as well as a sequence of 4 values that represent the boundaries defining the 5 different heart rate zones for a particular person. The function needs to finds out in what heart zone the bpm value falls into and return that zone (as a keyword - I later use that keyword in a map). If you're an experienced Clojure developer, what would you have done differently (and why) ? *(defn hr-zone* * Return the HR zone as a keyword according to the bpm value.* * [bpm [max-zone-1 max-zone-2 max-zone-3 max-zone-4]] * * (cond * *(= bpm max-zone-1) :hr-zone-1* *(and ( max-zone-1 bpm) (= max-zone-2 bpm)) :hr-zone-2 * *(and ( max-zone-2 bpm) (= max-zone-3 bpm)) :hr-zone-3 * *(and ( max-zone-3 bpm) (= max-zone-4 bpm)) :hr-zone-4 * *( max-zone-4 bpm) :hr-zone-5))* FYI, here is how I call this function in the REPL: (def my-hr-zones-defs [120 150 165 180]) (hr-zone 115 my-hr-zones-defs) (hr-zone 133 my-hr-zones-defs) (hr-zone 161 my-hr-zones-defs) (hr-zone 175 my-hr-zones-defs) (hr-zone 192 my-hr-zones-refs) Questions I have: Would that make sense to consider (and maybe enforce) that the sequence received as parameter is sorted? If this was the case, I would assume I could avoid the and calls- assuming that all the conditions in the cone form are evaluated in order. Assuming that I need to test bpm against the two boundaries of each range, would there be a better way to do this: *(and ( max-zone-1 bpm) (= max-zone-2 bpm))* ? Maybe this would work, but is it preferable? *( max-zone-1 bpm (dec **max-zone-2**))* *Thanks in advance for the advice.* *Laurent. * -- 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 unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.