Haskell - for loop

28,463

Solution 1

Often, repetition that you would perform with a loop in a procedural language is accomplished with recursion in Haskell. In this case, you should think about what the result of the loop is. It appears to alternate between 0 and 1. There are several ways to do this in Haskell. One way is

alternatingList n = take n alternating0and1
alternating0and1 = 0 : alternating1and0
alternating1and0 = 1 : alternating0and1

Solution 2

There are a few options. First, you can rewrite the problem with naive recursion:

loop :: Int -> Int
loop n = loop' n 0
  where loop' 0 a = a
        loop' n a = loop' (n - 1) (1 - a)

Next, you can restate recursion as a fold:

loop :: Int -> Int
loop n = foldr (\a _ -> 1 - a) 0 [0..n]

Or you can use State to simulate a for loop:

import Control.Monad
import Control.Monad.State

loop :: Int -> Int
loop n = execState (forM_ [0..n] 
                      (\_ -> modify (\a -> 1 - a))) 0

Solution 3

In Haskell instead of using loops, you combine standard library functions and/or your own recursive function to achieve the desired effect.

In your example code you seem to be setting a to either 0 or 1 depending on whether or not n is even (in a rather confusing fashion if I'm honest). To achieve the same in Haskell, you'd write:

a =
  if even n
  then 0
  else 1

Solution 4

The other answers have already explained how you would approach a problem like this functionally, in Haskell.

However, Haskell does have mutable variables (or references) in the form of ST actions and STRef. Using them is usually not very pretty, but it does allow you to express imperative, variable-mutating code faithfully in Haskell, if you really want to.

Just for fun, here's how you might use them to express your example problem.

(The following code also uses whileM_ from the monad-loops package, for convenience.)

import Control.Monad.Loops
import Control.Monad.ST
import Data.STRef

-- First, we define some infix operators on STRefs,
-- to make the following code less verbose.

-- Assignment to refs.
r @= x = writeSTRef r =<< x
r += n = r @= ((n +) <$> readSTRef r)

-- Binary operators on refs. (Mnemonic: the ? is on the side of the ref.)
n -? r = (-) <$> pure n <*> readSTRef r
r ?< n = (<) <$> readSTRef r <*> pure n


-- Armed with these, we can transliterate the original example to Haskell.
-- This declares refs a and x, mutates them, and returns the final value of a.
foo n = do
    a <- newSTRef 0
    x <- newSTRef 0
    whileM_ (x ?< n) $ do
        x += 1
        a @= (1 -? a)
    readSTRef a

-- To run it:
main = print =<< stToIO (foo 10)

Solution 5

Another option:

iterate (\a -> 1-a) 0 !! n
-- or even
iterate (1-) 0 !! n

The snippet iterate (\a -> 1-a) 0 produces an infinite lazy list of all the values obtained starting from 0 and repeatedly applying the function (\a -> 1-a). Then !! n takes the n-th element.

To be completely honest, in this case, I'd also look for a stricter definition of iterate which does not create so many lazy thunks.

Share:
28,463
Joe
Author by

Joe

Updated on March 04, 2020

Comments

  • Joe
    Joe about 4 years

    if I want to express something like [just a simple example]:

    int a = 0;
    for (int x = 0; x < n; x += 1)
        a = 1 - a;
    

    what should I do in Haskell, since it doesn't have variable concept? (maybe wrong, see: Does Haskell have variables?)