代码之家  ›  专栏  ›  技术社区  ›  Jonno_FTW

哈斯克尔清单建设

  •  1
  • Jonno_FTW  · 技术社区  · 16 年前

    对于Project Euler,我有以下递归函数 question no. 74 :

    chain n | n `elem` xs = length xs
            | otherwise = (chain (sumFac n)) : xs
    fac n = foldl (*) 1 $ enumFromTo 1 n
    sumFac n = sum $ map fac $ decToList n
    

    但我不知道在上面构造列表的正确语法 chain n 这样它就建立了一个 xs 然后返回 XS型 一旦一个数字再次出现在 XS 然后开始循环。

    如何更正我的链函数以使其正常工作?

    3 回复  |  直到 11 年前
        1
  •  5
  •   Greg Bacon    16 年前

    使用助手函数:

    chain n = go [n]
      where go xs | next `elem` xs = reverse $ next : xs
                  | otherwise      = go (next:xs)
              where next = sumFac $ head xs
    
    fac = product . enumFromTo 1
    
    sumFac = sum . map fac . map digitToInt . show
    

    正如你所看到的,你已经接近你想要的了,但你却把两者混在了一起。

    为了好玩,我投了点免费的等价物 fac 和 sumFac .

    下面是一个使用 view pattern (但是,唉,似乎很痒 #2395 ):

    {-# LANGUAGE ViewPatterns #-}
    
    chain = head . filter hasDup . tail . inits . iterate sumFac
      where hasDup (reverse -> (x:xs)) = x `elem` xs
    
        2
  •  1
  •   Paul Johnson    16 年前

    我觉得你的“链条”功能很混乱。你需要重新考虑一下。您在其中使用的值“xs”似乎不在范围内。“xs”是从哪里来的?大概这应该是一个论点。

    一种更好的方法是建立由问题生成的无限数字列表,然后检测其中的循环。您可以使用获得起始值“n”的无限列表

    numberSequence n = iterate sumFac n
    

    然后寻找周期。你必须对照前面所有的数字检查每个数字。一种简单但效率低下的方法是在进行时建立一个列表,对照当前列表检查每个数字,然后在递归调用中将该数字预先放到列表中。更有效的解决方案是使用data.set。

    顺便说一下,fac n=产品[1..n]。您的版本可以工作,但它不必过于冗长。(实际上,如果你用“产品”和“脱硫剂”[1..n]的定义来代替,你就得到了你的版本)。

        3
  •  0
  •   Chuck Vose    16 年前

    不知道,如果这很简单,但是你没有指定你想要一个头部和尾部,而不是参数中的整个列表。

    chain [n:xs] | n `elem` xs = length xs
                 | otherwise = chain (sumFac n) : xs
    fac n = foldl (*) 1 $ enumFromTo 1 n
    sumFac n = sum $ map fac $ decToList n
    

    我没有谴责者,所以我没有测试这是否有效。很酷,不过,我读了很多书。