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