Haskell - for 循环

Haskell - for loop

如果我想表达类似[只是一个简单的例子]:

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

Haskell,没有变量概念怎么办?(可能有误,见:Does Haskell have variables?

在 Haskell 中,您不使用循环,而是结合标准库函数 and/or 您自己的递归函数来实现所需的效果。

在您的示例代码中,您似乎将 a 设置为 0 或 1,具体取决于 n 是否为偶数(老实说,这是一种相当混乱的方式)。要在 Haskell 中实现同样的效果,您需要编写:

a =
  if even n
  then 0
  else 1

通常,在过程语言中用循环执行的重复是在 Haskell 中用 recursion 完成的。在这种情况下,您应该考虑循环的结果是什么。它似乎在 0 和 1 之间交替。在 Haskell 中有几种方法可以做到这一点。一种方法是

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

有几个选项。首先,你可以用朴素的递归重写问题:

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

接下来,您可以将递归重述为折叠:

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

或者你可以使用State来模拟一个for循环:

import Control.Monad
import Control.Monad.State

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

另一个选项:

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

代码段 iterate (\a -> 1-a) 0 生成一个无限惰性列表,其中包含从 0 开始并重复应用函数 (\a -> 1-a) 获得的所有值。然后!! n取第n个元素。

老实说,在这种情况下,我还会寻找更严格的 iterate 定义,它不会产生那么多懒惰的声音。

其他答案已经在 Haskell.

中解释了如何从功能上解决这样的问题

但是,Haskell 确实有 ST actions and STRef 形式的可变变量(或引用)。使用它们通常不是很漂亮,但它确实允许您在 Haskell 中忠实地表达命令式、可变变异代码,如果您真的想要的话。

只是为了好玩,以下是您可以如何使用它们来表达您的示例问题。

(为了方便起见,以下代码也使用了whileM_ from the monad-loops包。)

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)