【问题标题】:Haskell - for loopHaskell - for 循环
【发布时间】:2016-08-24 19:42:08
【问题描述】:

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

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

我应该在 Haskell 中做什么,因为它没有变量概念?(可能是错误的,请参阅:Does Haskell have variables?

【问题讨论】:

  • 你可以用recursion来做到这一点。
  • 附注Haskell 有变量。它们只是行为与程序语言不同。
  • @Code-Apprentice 呃,比如rec x a = if x &lt; n then rec x 1-a else ...?太棒了!
  • 你还需要在某处定义n
  • 我认为这很大程度上取决于你为什么要循环。有时,您可以使用抽象出迭代的高阶函数。其他时候,您编写自己的递归函数。还有一些,你实际上根本不需要迭代。

标签: loops variables for-loop haskell immutability


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

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

      在您的示例代码中,您似乎将 a 设置为 0 或 1,具体取决于 n 是否为偶数(老实说,这是一种相当令人困惑的方式)。为了在 Haskell 中达到同样的效果,你可以这样写:

      a =
        if even n
        then 0
        else 1
      

      【讨论】:

        【解决方案4】:

        另一种选择:

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

        sn-p iterate (\a -&gt; 1-a) 0 生成从0 开始并重复应用函数(\a -&gt; 1-a) 获得的所有值的无限惰性列表。然后!! n 取第n 个元素。

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

        【讨论】:

          【解决方案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)
          

          【讨论】:

            猜你喜欢
            • 2011-08-21
            • 1970-01-01
            • 2020-03-09
            • 2013-05-14
            • 1970-01-01
            • 2022-06-13
            • 2018-08-06
            • 1970-01-01
            • 2020-07-10
            相关资源
            最近更新 更多