是否有更干净,优雅的Haskell方式来编写这个LCM功能?

时间:2015-08-23 02:32:53

标签: haskell recursion fold readability lcm

我刚开始使用Haskell并敲定了这个简单的递归算法,为列表中的每个数字找到了LCM。它有效,但它很混乱,我希望有一些同行评论如何使这更优雅,可读和Haskell-y。

lcms list 
  | length list > 1 = lcms (lcm (head list) (head (tail list)):(tail (tail list)))
  | otherwise = list

因此,它采用一个列表并对前两个项目进行LCM,然后将其预先列出减去这两个元素。基本上,我想要的psudocode是这样的:

lcms [a,b,c] = lcm (a, (lcm (b, c))

任何建议,有人吗?我渴望在Haskell上进行改进并撰写人们可以阅读的内容。效率提示也很受欢迎!

谢谢,全部!

2 个答案:

答案 0 :(得分:9)

它是一个折叠:

lcm

其中lcm :: Int -> Int -> Int 只计算两个数字的lcm:

onclick

答案 1 :(得分:3)

您可以使用您建议的语法编写它,因此:

lcms (a:b:c) = lcms (lcm a b:c)
lcms list = list

我发现第二个条款有点奇怪,但并不可怕:它优雅地处理空列表,但是当你知道你最多会返回一个项目时返回一个列表可能会被某些hasochists看作有点不精确类型。您还可以考虑使用Maybe,规范的0或1元素类型:

lcms (a:b:c)  = lcms (lcm a b:c)
lcms [answer] = Just answer
lcms []       = Nothing

另一个好的选择是找出一个理智的基础案例。对于二进制操作,操作的单位通常是一个不错的选择,因此对于lcm,我会选择1,因此:

lcms (a:b:c)  = lcms (lcm a b:c)
lcms [answer] = answer
lcms []       = 1

通常,尽可能避免显式递归;另一个答案显示了如何采取这一步骤。在这种情况下,有一个中间转换,使基本情况稍微美观一些:而不是将累加器保持在列表的头部 - 这只是偶然的,因为你的累加器与列表元素的类型相同 - 一个可以使积累更明确或更少。因此,其中一个:

lcms (x:xs) = lcm x (lcm xs)
lcms []     = 1

-- OR

lcms = go 1 where
    go acc (x:xs) = go (lcm acc x) xs
    go acc []     = acc

这两个实现对应于选择foldrfoldl来消除显式递归; foldl'与第二个类似,但需要额外的seq

lcms = go 1 where
    go acc (x:xs) = let acc' = lcm acc x in acc' `seq` go acc' xs
    go acc []     = acc