I am trying to write a simple iterating algorithm in Haskell, but I'm struggling to find the optimal solution in terms of elegance and speed.
I have an algorithm that needs to apply an operation to a state over a number of iterations until some stopping condition is reached, recording the state using some arbitrary function. I already know how to implement a scheme like this by defining a function like iterateM.
But in this case the operation to perform for each step depends on the state, and boils down to checking a 'step type' condition to decide on the next iteration types, and then performing operation A for the next 10 iterations, or performing operation B for the next iteration before checking the condition again.
I could write it in an imperative style as:
c=0
while True:
if c>0:
x=iterateByA(x)
c=c-1
else:
if stepCondition(x)==0:
x=iterateByA(x)
c=9
else:
x=iterateByB(x)
observeState(x)
if stopCondition(x):
break
and of course this could just be copied in Haskell, but I would rather do something more elegant.
My idea is to have the iteration use a list of functions to pop and apply to the state, and update that list with a new one (based on the 'step type' condition) once it is empty. I'm slightly concerned that this will be inefficient though. Would doing this and using something like
take 10 (repeat iterateByA)
compile away all of the list allocation etc to a tight loop that only uses a counter, like the imperative one above?
Is there another neat and efficient way of doing this?
If it helps this is for an adaptive stochastic simulation algorithm, the iteration steps update the state and the step condition (that decides the best simulation scheme) is a function of the current state. There are infact 3 different iteration schemes but I figured that an example with 2 is easier to explain.
(I'm not sure if it matters but I should probably also point out that in haskell the iterateByX functions are monadic since they use random numbers.)
A direct translation doesn't look too bad.
loop c x
| stopCondition x = observe x
| c > 0 = observe x >> iterateByA x >>= loop (c-1)
| stepCondition x = observe x >> iterateByA x >>= loop 9
| otherwise = observe x >> iterateByB x >>= loop c
The repetition of observe
can be removed via various tricks if you don't like it.
You should probably rethink things, though. This is a very imperative approach; probably something much better can be done (but it's hard to say how from the few details you've given here).
observeState
on the mutated version ofx
whereas your version callsobserve
on the pre-mutated version. Also thestopCondition
case is tested at the end of the loop, not the start - pat 2012-04-04 20:14