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)
如果我想表达类似[只是一个简单的例子]:
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)