Haskell - for循环

Joe*_*Joe 3 variables haskell loops for-loop immutability

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

int a = 0;
for (int x = 0; x < n; x += 1)
    a = 1 - a;
Run Code Online (Sandbox Code Playgroud)

我应该在Haskell中做什么,因为它没有可变的概念?(也许是错的,请参阅:Haskell有变量吗?)

use*_*560 9

有几个选择.首先,您可以使用朴素递归重写问题:

loop :: Int -> Int
loop n = loop' n 0
  where loop' 0 a = a
        loop' n a = loop' (n - 1) (1 - a)
Run Code Online (Sandbox Code Playgroud)

接下来,您可以将递归重新表示为折叠:

loop :: Int -> Int
loop n = foldr (\a _ -> 1 - a) 0 [0..n]
Run Code Online (Sandbox Code Playgroud)

或者您可以使用State模拟for循环:

import Control.Monad
import Control.Monad.State

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


Cod*_*ice 8

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

alternatingList n = take n alternating0and1
alternating0and1 = 0 : alternating1and0
alternating1and0 = 1 : alternating0and1
Run Code Online (Sandbox Code Playgroud)


sep*_*p2k 6

在Haskell而不是使用循环中,您可以组合标准库函数和/或您自己的递归函数来实现所需的效果.

在您的示例代码中,您似乎设置a为0或1,具体取决于是否n为偶数(如果我诚实,则以相当混乱的方式).要在Haskell中实现相同的目的,你需要写:

a =
  if even n
  then 0
  else 1
Run Code Online (Sandbox Code Playgroud)


chi*_*chi 5

另外一个选项:

iterate (\a -> 1-a) 0 !! n
-- or even
iterate (1-) 0 !! n
Run Code Online (Sandbox Code Playgroud)

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

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


Pi *_*ort 5

其他答案已经解释了如何在 Haskell 中从功能上解决这样的问题。

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

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

(为方便起见,以下代码还使用了monad-loops包中的whileM_。)

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)
Run Code Online (Sandbox Code Playgroud)