我试图编写一个函数来计算从1到n的所有数字的Collatz序列。结果应该是列表清单的形式。
这样的事情:
collatzSeqs 5 => [[1], [2, 1], [3, 10, 5, 16, 8, 4, 2, 1], [4, 2, 1], [5, 16, 8, 4, 2, 1]]
我的代码如下所示:
nextCollatz:: Int-> Int
nextCollatz n
| n < 1 = error "number lower than 1"
| n == 1 = 1
| mod n 2 == 0 = div n 2
| otherwise = 3 * n + 1 -- Calculates the next Collatz number
collatzSeq:: Int-> [Int]
collatzSeq n
| n < 1 = error "number lower than 1"
| n == 1 = [1]
| otherwise = n:collatzSeq (nextCollatz n) -- Makes a sequence a collatz sequence
我必须编写collatzSeqs
的想法是采用head[collatzSeq]
并以某种方式继续以递归形式执行它,然后获取结果并将其插入可能为空的新列表中。
为了说明我的想法,我试图将其编码:(它没有用)
collatzSeqs :: [Int] -> [[Int]]
collatzSeqs n = collatzSeq head[1..n]: []
答案 0 :(得分:2)
查看您对collatzSeqs
的尝试,我们看到它需要输入n
并返回包含数字1
的列表列表。这源于这里实际上没有递归调用的事实。所有函数都是一个整数,并将1个consed的collatz序列返回到一个空列表中,如下所示:
-- example 3:
collatzSeqs 3 = collatzSeq (head [1..n]) : [] = collatzSeq 1 : [] = [1] : [] = [[1]]
我们可以看到,对于任何输入,该函数将只返回[[1]]
。问题是这里没有递归。如果我们要重写这个函数,我们会得到这样的结果:
collatzSeqs n = collatzHelp n 1
where
collatzHelp n m
| n == m = collatzSeq n : []
| otherwise = collatzSeq m : collatzHelp n (m+1)
但这是复杂的方法,显然必须有一些更简单的东西,因为我们想做的只是取一个函数并将它应用于列表中的每个元素。幸运的是,确实存在!
您的collatzSeqs
实际上只是一张简单的地图:
collatzSeqs n = map collatzSeq [1..n]
map是一个函数,它接受函数和列表,并返回应用于给定列表的每个元素的函数的结果列表。您可以想象它的定义如下:
map f [] = []
map f (x:xs) = f x : map f xs
在这种情况下,我们希望为每个元素计算的函数是collatzSeq
,我们的列表应该是从1到n的数字,因为我们希望函数应用于包含1和n之间的每个数字。
答案 1 :(得分:0)
通过结打结。
import Control.Applicative ((<*>))
import Control.Monad (forM_)
collatz' :: [[Integer]]
collatz' = map ((:) <*> next) [1..]
where
next n = collatz . fromInteger $ case n `quotRem` 2 of
(d, 0) -> d
(_, 1) -> 3 * n + 1
collatz :: Int -> [Integer]
collatz = (collatz' !!) . pred
main :: IO ()
main = do
nStr <- getLine
let n = read nStr
forM_ [1..n] $ \i -> do
putStr $ show i
putStr ": "
putStr . show . take n $ collatz i
putStrLn "..."
它只计算一次Integer的Collatz后继。
不会将序列停在1,而是循环,这就是take
的原因。重构为1停止作为练习。