在haskell中递归期间打印结果

时间:2009-08-05 08:26:00

标签: haskell

我开始学习Haskell并正在制作程序来完成迭代过程:

n -> n/2 (for even n)
n -> 3n+1 (for odd n)

所以我得到了这个:`

chain n     | n == 0       = error "What are you on about?"
            | n == 1       = error "Finished"
            | rem n 2 == 0 = chain (n `div` 2) 
            | rem n 2 /= 0 = chain (3 * n + 1)

` 这会有用吗? 但它只在幕后进行计算,有没有办法让它显示或导出为每次迭代时n的结果,直到它达到1,这样我可以在以后找到列表的长度?

另外,有没有办法让GHCi在特定的文件夹中开始?(我正在使用Windows)

5 个答案:

答案 0 :(得分:2)

你可以让它返回“链”中的结果列表,如下所示:

chain n     | n == 0       = error "What are you on about?"
            | n == 1       = []
            | rem n 2 == 0 = (n `div` 2) : chain (n `div` 2)
            | otherwise    = (3 * n + 1) : chain (3 * n + 1)

现在你会得到这样的结果:

*Main> chain 3
[10,5,16,8,4,2,1]
*Main> chain 7
[22,11,34,17,52,26,13,40,20,10,5,16,8,4,2,1]

答案 1 :(得分:2)

是的,当然。甚至Data.List模块中的一个函数也捕获了这种使用模式。

import Data.List (unfoldr)

chain 0 = []
chain n = unfoldr f n
  where
    f 1 = Nothing
    f n = let n' | even n    = n `div` 2
                 | otherwise = 3 * n + 1
          in Just (n', n')

展开者的类型是:(b - > Maybe(a,b)) - > b - > [α]

b是用于生成列表的状态变量。作为unfoldr的第一个参数提供的函数应该返回Nothing(表示终止列表)或Just(b,a),其中a是要添加到列表的元素,b是新的状态变量。

答案 2 :(得分:2)

  

有没有办法让它在每次迭代时显示或导出为n的结果,直到达到1

您可以使用Debug.Trace.trace在计算值时将记录消息打印到stdout。适合快速调试。

答案 3 :(得分:1)

您会注意到前两个答案使用“构建整个列表,然后在结尾处输出”而不是“一次输出一个元素”的范例。这是功能性的做事方式。

如果你真的想以命令式方式进行,你必须使用monadic编程,这是一个更高级的主题。这是一个例子(不要担心,如果你无法理解正在发生的一切...... monad是非常神秘和神奇的):

import Control.Monad.Writer

chain :: Int -> (String, [Int])
chain = runWriter . chain'
    where
      chain' 0 = return "Failure"

      chain' 1 = do
        tell [1]         -- write 1
        return "Success" -- finished

      chain' n = do
        -- write n
        tell [n]
        -- calculate next n and recurse
        if even n
           then chain' $ n `div` 2
           else chain' $ 3 * n + 1

结果如下:

*Main> chain 3
("Success",[3,10,5,16,8,4,2,1])

但是,正如你所看到的,作家monad仍然只是在幕后生成列表。

这似乎效率低下。如果要打印1,000,000个元素列表怎么办?你真的必须提前生成整个列表吗?事实上,Haskell的懒惰语义意味着,只要有可能,Haskell就会编译你的“构建整个事物,然后将其打印出来”代码“只生成并输出一个元素”代码。

答案 4 :(得分:1)

该函数应该只返回找到的元素列表。此列表可在以后进一步检查:

chain 0 = error "No no no."
chain 1 = [1]
chain n
  | rem n 2 == 0 = n : chain (n `div` 2) 
  | otherwise    = n : chain (3 * n + 1)

此处[1]是一个仅包含1的列表,:是一个列表构造函数,它将左侧的元素添加到右侧的列表中。

chain构建的列表随后可以显示或与其他功能一起使用:

*Main> chain 5
[5,16,8,4,2,1]
*Main> length (chain 31)
107