How to resolve the algorithm Averages/Simple moving average step by step in the Haskell programming language

Published on 7 June 2024 03:52 AM

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