views:

81

answers:

2

Hi there,

I have the following problem: I have a time-series with more than 10000 entries and I want to perform some calculations with each of them. This alone wouldn't be a problem, but I need to get the last calculated value in order to get the next one. A very simple form of what I need would look like this:

Val(n) = Val(n-1) + (time-series-entry / 2) (or something like it!)

I don't have any idea how to manage this. Simply doing something like this:

(defn calc-val
  [time-series element]
  (seq (cons (generate-val-element time-series element)
             (calc-val time-series (inc element)))))

wouldn't work because can't (at least I don't know how!) get the last computed value. Then I thought: OK, let's use Loop-Recur. This would give me the value corresponding to the time-series entry BUT for the next one I would have to do all the computations again. Iterate would be the right thing, but it didn't work because the function has side effects.

So I'm stuck here on this one. It would be great if someone could give me a hint.

+3  A: 

If you just want a hint; look into using partition.

For a little more than a hint

(defn calc-val
  [time-series element]
  (let [p (partition 2 1 time-series)]
    (for [t p]
      (let [first-value (first t)
            second-value (second t)]
        (do whatever you need to here)))))

Though this hasn't been tested, it should work or be close to working :)

Explanation

(partition n i seq) separates seq into parts that lists of length n (2 in this case) with overlap i (1 in this case), and then we iterate over those with for, and do what we want with the parts.

Isaac Hodes
If I understand the question correctly, the transformation result on each step depends on the transformation result on the previous step -- not on the previous untransformed entry. Thus indirectly it depends on the entire initial fragment of the series seq preceding it. (Hoping to be corrected by the OP if I'm wrong. :-))
Michał Marczyk
If that's the case, absolutely. I understood it differently (as his imperative example made it seem otherwise) but I'm happy to be wrong or right! Either way, he's in good hands ;)
Isaac Hodes
It seems so to me too. Here's hoping that s/he agrees. :-)
Michał Marczyk
Actually it occurs to me now that I'd probably write `(map do-stuff-to-two-entries entries (rest entries))` to do this... Possibly wrapped in a `dorun` for side effects and/or `(cons (first entries) ...)` if I wanted to keep the first entry in the result. (I prefer `doseq` for side-effecty iteration over a single collection, but when there's more and I need to traverse them in parallel, `map` is very handy.)
Michał Marczyk
+5  A: 

If you only care about the final result, use reduce; if you need to get a seq of results of transforming each value in turn (where each transformation depends on the previous ones), use reductions (found in clojure.contrib.seq-utils in 1.1 and in clojure.core in 1.2).

Below, transform-first-entry does whatever you want to do to the first entry (if you don't need to transform it in any way, you can just leave out the first argument to reduce / reductions and use entries rather than (rest entries as the final argument); transform-entry is the function which takes the result of transforming the previous entry and the current entry (in this order) and produces the transformation result for the current entry.

;;; only care about the final result
(reduce transform-entry
        (transform-first-entry (first series))
        (rest entries))

;;; need to get a seq of intermediate results
(reductions ...arguments as above...)

Note that reductions is lazy.

Assuming you wanted to leave the first entry unchanged and apply your example transformation from the question text to the subsequent entries, you could use

(defn transform-entry [prev-transformed current]
  (+ prev-transformed
     (/ current 2)))

as the reduction function in

(reduce transform-entry series) ; ...or reductions
Michał Marczyk