I have this pseudocode that calculates the factorial of a number given by the user.
Process without_title
Define i, tmp, x As Integers;
tmp<-1;
Write "Enter a number to calculate its factorial";
Read x;
For i <- 1 Until x Do
tmp<-tmp * i;
EndTo
Write tmp;
EndProcess
Now I would like to implement the same code in Haskell using list comprehensions
import Data.IORef
factorial::Int -> Int
factorial x = [tmp|i<-[1..x], modifyIORef tmp (*i)]
Throws error saying tmp variable not defined
How do I define the tmp variable, so that I can use it?
To implement a factorial function using a mutable IORef like this you will need to work in the IO monad and use a newIORef operation to create your IORef and then use forM_, traverse_, or sequence_ or a similar function to sequence a set of IO operations on that IORef. It might look something like:
import Control.Monad
import Data.IORef
factorial :: Int -> IO Int
factorial x = do
tmp <- newIORef 1
forM_ [1..x] $ \i -> modifyIORef' tmp (*i)
readIORef tmp
or, if you prefer to use a list comprehension so it looks a little more like your attempt above, something like:
import Data.IORef
factorialIO :: Int -> IO Int
factorialIO x = do
tmp <- newIORef 1
sequence_ [modifyIORef' tmp (*i) | i <- [1..x]]
readIORef tmp
(Using the strict modifyIORef' provides better performance than the lazy modifyIORef.)
This can then be called like so:
main :: IO ()
main = do
result <- factorial 10
print $ result
This will generally be slower than a more direct, "pure" solution like:
factorial :: Int -> Int
factorial x = product [1..x]
My benchmarks suggest that the IORef-based version is about two times slower than the pure product-based version.
To complement the existing answers: a problem is that if you start involving IO, you cannot have an IO-free type signature like factorial :: Int -> Int for your function.
Another (easier to deal with) problem is that if you return a list comprehension expression directly, the result type of your function has to be some sort of list. It cannot be a scalar value such as Int.
A possible compromise is to use the Haskell state monad, with the tmp product as the state. You can generate the list of required multiplication operations with a suitable list comprehension expression, and then combine them using the sequence_ library function.
Like this:
import Control.Monad.State (state, runState, sequence)
factorial :: Int -> Int
factorial x = finalTmp where
tmp0 = 1 -- our initial state
actions = [ state (\tmp -> ((), i*tmp)) | i <- [1..x] ]
(rs, finalTmp) = runState (sequence_ actions) tmp0
using tmp as the state as per your notations, even though using st would be more idiomatic.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With