我开始学习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)
答案 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