Haskell - for loop

杀马特。学长 韩版系。学妹 提交于 2019-12-06 03:18:40

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

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

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

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.

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)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!