Reputation: 34884
I'm curious about implementing a loop in Haskell. How can I do something similar in Haskell (pseudo-code):
var i = 0
for (int i1 = 0; i1 < 10; i1++) {
println(i1)
i += 2
}
println(i)
Upvotes: 2
Views: 2763
Reputation: 24802
In functional terms what you are doing is folding over a list of integers so that for each integer you print the element and increase an accumulator by 2. Since we are printing something (i.e. doing I/O) we need to fold in a monad but otherwise it's just your standard left-fold.
foldM (\i i1 -> print i1 >> return (i + 2)) 0 [0..9] >>= print
We fold with a lambda function that uses the same variable names as your code. I.e. i1
is the current element and i
is the accumulator.
The next parameter is the initial value for the accumulator which corresponds to i = 0
in your code.
The final parameter is the (inclusive on both ends) list of numbers we fold over.
The >>=
(bind operator) pipes the result of the fold (i.e. the final value of the accumulator i
) to the print function.
EDIT: This is assuming that you meant to write
var i = 0
for (int i1 = 0; i1 < 10; i1++) {
println(i1)
i += 2
}
println(i)
instead of incrementing just i
in both the for-clause and loop body.
Upvotes: 8
Reputation: 15028
Going by the two assumptions that
(the same assumption shang made) that you want to print out
0
1
2
3
4
5
6
7
8
9
20
I would in Haskell write
do
let xs = [0..9]
mapM_ print xs
print (length xs * 2)
You see how the original computation got split up into three separate (and independent!) computations.
i1
into a list. We know the bounds are 0 and 9 inclusive, so the loop variable can be represented by the list [0..9]
.i
" from what we know about the list, and then print it as well.The third computation is especially interesting, because it highlights the difference between traditional imperative programming and Haskell, which is a lot about declarative programming.
Adding two to a number every iteration of a list is the same thing as taking the length of the list and multiplying it by two. The big difference, in my eyes, is that I can read i = length xs * 2
and understand what it means in the blink of an eye. Counting i
up every iteration of a loop, however, takes some thinking to understand what it really means.
The fact that all three sub-computations are independent means they are a lot easier to test – you can test them one at a time and if they work individually, they will work together as well!
If you meant "similar" in the sense of "similar-looking code", refer to any of the STRef
/IORef
answers.
Upvotes: 7
Reputation: 8162
An easy yet flexible way to do a loop is to define a recursive function in a let or where clause:
main = loop 0 10
where
loop i n | i < n = putStrLn (show i) >> loop (i+2) n
loop _ _ = return ()
This defines a function loop
of two variables i
and n
. The first pattern has a guard (after the |
), which checks the condition (i < n
). If it is true this branch is selected and i
is printed to the console before loop
calls itself again now binding i
to i+2
. Otherwise the default branch is selected, which just returns ()
(=do nothing in the IO Monad).
Implementing loops like this with recursive functions is pretty flexible. If it is easy you want to go with higher-order-functions (such as for
and map
), but if you can't figure out how to translate some loop from an imperative setting, recursive functions can usually do the trick.
Upvotes: 1
Reputation: 120711
import Data.IORef
import Control.Monad
main = do
i <- newIORef 0
let loop = do
iNow <- readIORef i
when (iNow < 10) $ do
print i
modifyIORef i (+1)
loop
loop
But obviously, you should avoid this. mapM_ print [0..9]
works much better.
I see you have to i
variables in there with different increment. Well, it's obvious how to add that here. You can stay to the manual recursion through loop. A little better is replacing one IORef
by a simple forM_
. Preferrably, try not to use any IORef
at all but only functional structures.
Upvotes: 1
Reputation: 68152
You would use a higher-order function like forM_
. The name looks a little weird, but it's actually very systematic: the function is called for
, it works on monads (M
) and it does not return a value (_
): forM_
.
You could use it like this:
import Control.Monad
import Data.IORef
main = do i <- newIORef 0
forM_ [0..9] $ \ i' -> do
print i'
i `modifyIORef` (+ 2)
readIORef i >>= print
The forM_
function itself can be implemented with recursion. Here's a simple way to do it:
forM_ [] _ = return ()
forM_ (x:xs) f = f x >> forM_ xs f
Of course, using mutable state like this in Haskell is ugly. Not because it has to be ugly, but because it's so rarely used. You could imagine a C-like library that looked much nicer; take a look at this example.
Naturally, the best way to do this in Haskell is to take a more functional approach and forget about mutable variables. You could write something like this instead:
main = do forM_ [0..9] print
print $ sum [i' * 2 | i' <- [0..9]]
We could also improve the looping code with some very simple function definitions. Just having a nice operator for reading, setting and modifying IOVar
s goes a long way:
(!) :: (a -> IO b) -> IORef a -> IO b
f ! var = readIORef var >>= f
(+=) :: Num a => IORef a -> a -> IO ()
var += n = var `modifyIORef` (+ n)
ref :: a -> IO (IORef a)
ref = newIORef
This lets us write the loop as so:
import Data.IORef
main = do i <- ref 0
forM_ [0..9] $ \ i' -> do
print i'
i += 2
print !i
At this point, it almost looks exactly like OCaml! Quite an improvement for just a few operator definitions.
Upvotes: 3