我刚开始使用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上进行改进并撰写人们可以阅读的内容。效率提示也很受欢迎!
谢谢,全部!
答案 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
这两个实现对应于选择foldr
或foldl
来消除显式递归; foldl'
与第二个类似,但需要额外的seq
:
lcms = go 1 where
go acc (x:xs) = let acc' = lcm acc x in acc' `seq` go acc' xs
go acc [] = acc