我的目标是编写一个函数,该函数计算某个特定数字“ n”以下的最大Collat​​z数字。 (对于熟悉的人来说,这是欧拉计画的问题。)

一些上下文:给定整数的Collat​​z数等于该整数的Collat​​z序列的长度。整数的Collat​​z序列计算如下:序列中的第一个数字(“ n0”)是该整数本身;如果n0是偶数,则序列中的下一个数字(“ n1”)等于n / 2;如果n0是奇数,则n1等于3 * n0 +1。我们继续递归扩展序列,直到到达1,此时序列结束。例如,5的collat​​z序列为:{5,16,8,8,4,2,1}(因为16 = 3 * 5 + 1,8 = 16/2,4 = 8/2,...)

我正在尝试编写一个函数(“ maxCollat​​zUnder”),该函数在传递整数“ m”时会返回具有最长Collat​​z序列(即最大Collat​​z编号)的整数(小于或等于m)。例如,maxCollat​​z 20(即(包含)20以下的整数具有最长的拼贴序列?)应返回19(数字19的Collat​​z序列长度为21:[19,58,29,88,44,22, 11,34,17,52,26,13,40,20,10,5,16,8,4,2,1])。

在下面的代码中,“ collat​​z”和“ collat​​zHelper”函数可以正确编译并运行。我在使用“ maxCollat​​zUnder”函数时遇到麻烦。此函数旨在(I)为范围从1到m的每个整数x创建一个2元组(x,y)的列表(其中m是函数参数),其中y代表整数x的Collat​​z数,然后( II)浏览列表中最高的Collat​​z数(即y),并返回其关联的整数(即x)

maxCollatzUnder n = foldl(\acc (i,j) -> if j > acc then i else acc) 0
    (zip [1..n] ( map collatzLength [1..n]))
    where collatzLength n = length . collatz $ n

collatz n = map truncate $ collatzHelper n

collatzHelper 0 = [0]
collatzHelper 1 = [1]
collatzHelper n
    | (truncate n) `mod` 2 == 0 = [n] ++ collatzHelper (n/2)
    | otherwise = [n] ++ collatzHelper (3*n+1)


我(尝试)编译时收到以下错误。

*Main> :l PE14Collatz.hs
[1 of 1] Compiling Main             ( PE14Collatz.hs, interpreted )

PE14Collatz.hs:7:89:
    No instance for (RealFrac Int)
      arising from a use of `collatzLength'
    In the first argument of `map', namely `collatzLength'
    In the second argument of `zip', namely
      `(map collatzLength [1 .. n])'
    In the third argument of `foldl', namely
      `(zip [1 .. n] (map collatzLength [1 .. n]))'
Failed, modules loaded: none.


令人好奇的是,如果将“ maxCollat​​zUnder”更改为以下代码(请参见下文),则代码可以正确编译并运行。唯一的变化是,在以下版本中,fold函数返回的是“ j”(即最大的Collat​​z数),而不是“ i”(即产生最大的Collat​​z数的整数)。

maxCollatzUnder n = foldl(\acc (i,j) -> if j > acc then j else acc) 0
    (zip [1..n] ( map collatzLength [1..n]))
    where collatzLength n = length . collatz $ n


欢迎提出关于更有效/更优雅的方法的建议,尽管我仍然有兴趣了解此错误的原因。

最佳答案

由于使用了truncateRealFrac的方法)和/Fractional的方法,RealFrac的超类),Haskell为您的后两个函数推断出以下两个类型签名:

collatz :: (RealFrac a, Integral b) => a -> [b]
collatzHelper :: RealFrac a => a -> [a]


然后,Haskell尝试推断maxCollatzUnder的类型,其思考过程如下:


“在collatzLength n = length . collatz $ n中,我们将n传递给collatz,因此collatzLength的参数必须是RealFrac。”
“因此,在map collatzLength [1..n]中,[1..n]必须是RealFrac值的列表。”
“因此,n中的map collatzLength [1..n]必须是RealFrac类型。”
“因此,n中的zip [1..n](与n相同)必须是RealFrac类型,因此[1..n]RealFrac的列表。”
“因此,i中的(\acc (i,j) -> if j > acc then i else acc)必须是RealFrac。”
“由于上述lambda可以返回iacc,因此它们必须是相同的类型。”
“因为将jacc进行比较,所以j必须与acc属于同一类型-因此与iRealFrac属于同一类型。”
“但是等等-jcollatzLength的返回值,它是对length的调用的返回值,因此它必须是Int,但是Int不在!”
“错误!错误!”


我现在必须走(编译器阴谋不喜欢我泄露他们的秘密),但最短的解决方法是不使用RealFractruncate,而仅使用/进行(泛洪)整数除法。

10-04 20:20