How to resolve the algorithm Averages/Simple moving average step by step in the Haskell programming language
How to resolve the algorithm Averages/Simple moving average step by step in the Haskell programming language
Table of Contents
Problem Statement
Computing the simple moving average of a series of numbers. Create a stateful function/class/instance that takes a period and returns a routine that takes a number as argument and returns a simple moving average of its arguments so far. A simple moving average is a method for computing an average of a stream of numbers by only averaging the last P numbers from the stream, where P is known as the period. It can be implemented by calling an initialing routine with P as its argument, I(P), which should then return a routine that when called with individual, successive members of a stream of numbers, computes the mean of (up to), the last P of them, lets call this SMA(). The word stateful in the task description refers to the need for SMA() to remember certain information between calls to it:
Stateful also means that successive calls to I(), the initializer, should return separate routines that do not share saved state so they could be used on two independent streams of data. Pseudo-code for an implementation of SMA is:
Let's start with the solution:
Step by Step solution about How to resolve the algorithm Averages/Simple moving average step by step in the Haskell programming language
Source 1:
The first source code you provided uses the !
bang operator to indicate that the arguments to the Pair
constructor are strict. This means that they will be evaluated before the constructor is called, which can improve performance in some cases.
The mean
function calculates the mean (average) of a list of fractional numbers. It uses the foldl'
function to accumulate the sum and count of the numbers, and then divides the sum by the count to get the mean.
The series
list contains a sequence of numbers that will be used to calculate the moving averages.
The mkSMA
function takes an integer as an argument and returns an IO action that creates a new IORef of type Double
. The IORef will be used to store the moving average of the last period
numbers.
The main
function uses the mkSMA
function to create two IO actions that will calculate the 3-period and 5-period moving averages of the series
list. The mapM_
function is then used to apply the str
function to each element of the list, where str
takes a number, a 3-period moving average, and a 5-period moving average and returns a string.
Source 2:
The second source code you provided uses the scanl
function to calculate the moving average of the last p
numbers in a list. The scanl
function applies a binary function to each element of a list, accumulating a value from left to right. In this case, the binary function is (\(y,_) -> (id &&& return. av) . (: if length y == p then init y else y))
, which takes a pair of values, the current element of the list, and the accumulated value, and returns a new pair of values. The first value in the new pair is the updated accumulated value, and the second value is the current element of the list. The av
function is defined as liftM2 (/) sum (fromIntegral.length)
, which takes a sum and a count and returns their quotient.
The printSMA
function takes an integer n
and an integer p
as arguments, and returns an IO action that will print the first n
moving averages of the last p
numbers in a list. The mapM_
function is used to apply the (\(n,a) -> putStrLn $ "Next number: " ++ show n ++ " Average: " ++ show a)
function to each element of the list, where the function takes a number and a moving average and prints a string to the console.
Source 3:
The third source code you provided uses the State
monad to calculate the moving average of the last period
numbers in a list. The State
monad is a monad that allows us to track a state value as we compute a value. In this case, the state value is the list of previous values that we have seen.
The computeSMA
function takes a float as an argument and returns a State SMAState
action that will update the state and return the new moving average. The get
function is used to get the current state value, and the put
function is used to update the state value. The dropIf
function takes an integer and a list as arguments and returns a new list that contains all the elements of the original list except for the first x
elements.
The demostrateSMA
function is a State SMAState
action that will calculate the moving average of the last period
numbers in the list [1, 2, 3, 4, 5, 5, 4, 3, 2, 1]
. The main
function evaluates the demostrateSMA
function and prints the result to the console.
Source code in the haskell programming language
{-# LANGUAGE BangPatterns #-}
import Control.Monad
import Data.List
import Data.IORef
data Pair a b = Pair !a !b
mean :: Fractional a => [a] -> a
mean = divl . foldl' (\(Pair s l) x -> Pair (s+x) (l+1)) (Pair 0.0 0)
where divl (_,0) = 0.0
divl (s,l) = s / fromIntegral l
series = [1,2,3,4,5,5,4,3,2,1]
mkSMA :: Int -> IO (Double -> IO Double)
mkSMA period = avgr <$> newIORef []
where avgr nsref x = readIORef nsref >>= (\ns ->
let xs = take period (x:ns)
in writeIORef nsref xs $> mean xs)
main = mkSMA 3 >>= (\sma3 -> mkSMA 5 >>= (\sma5 ->
mapM_ (str <$> pure n <*> sma3 <*> sma5) series))
where str n mm3 mm5 =
concat ["Next number = ",show n,", SMA_3 = ",show mm3,", SMA_5 = ",show mm5]
import Data.List
import Control.Arrow
import Control.Monad
sMA p = map (head *** head ).tail.
scanl (\(y,_) -> (id &&& return. av) . (: if length y == p then init y else y)) ([],[])
where av = liftM2 (/) sum (fromIntegral.length)
printSMA n p = mapM_ (\(n,a) -> putStrLn $ "Next number: " ++ show n ++ " Average: " ++ show a)
. take n . sMA p $ [1..5]++[5,4..1]++[3..]
import Control.Monad
import Control.Monad.State
period :: Int
period = 3
type SMAState = [Float]
computeSMA :: Float -> State SMAState Float
computeSMA x = do
previousValues <- get
let values = previousValues ++ [x]
let newAverage = if length values <= period then (sum values) / (fromIntegral $ length remainingValues :: Float)
else (sum remainingValues) / (fromIntegral $ length remainingValues :: Float)
where remainingValues = dropIf period values
put $ dropIf period values
return newAverage
dropIf :: Int -> [a] -> [a]
dropIf x xs = drop ((length xs) - x) xs
demostrateSMA :: State SMAState [Float]
demostrateSMA = mapM computeSMA [1, 2, 3, 4, 5, 5, 4, 3, 2, 1]
main :: IO ()
main = print $ evalState demostrateSMA []
You may also check:How to resolve the algorithm Delete a file step by step in the Factor programming language
You may also check:How to resolve the algorithm Reverse words in a string step by step in the CoffeeScript programming language
You may also check:How to resolve the algorithm Gaussian elimination step by step in the Lobster programming language
You may also check:How to resolve the algorithm Read entire file step by step in the Common Lisp programming language
You may also check:How to resolve the algorithm Call a function step by step in the BBC BASIC programming language