如果我想表达像[只是一个简单的例子]:
int a = 0;
for (int x = 0; x < n; x += 1)
a = 1 - a;
我应该在Haskell中做什么,因为它没有可变的概念? (也许是错的,请参阅:Does Haskell have variables?)
通常,在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
在Haskell而不是使用循环中,您可以组合标准库函数和/或您自己的递归函数来实现所需的效果。
在您的示例代码中,您似乎将a
设置为0或1,具体取决于n
是否为偶数(如果我诚实,则会以相当混乱的方式)。要在Haskell中实现相同的目的,你需要写:
a =
if even n
then 0
else 1
另外一个选项:
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行为和STRef形式的可变变量(或引用)。使用它们通常不是很漂亮,但它确实允许你在Haskell中忠实地表达命令式,变量变异代码,如果你真的想要的话。
只是为了好玩,以下是如何使用它们来表达您的示例问题。
(为方便起见,下面的代码也使用了whileM_包中的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)