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.

Reply via email to