是否有更简洁、优雅、Haskell 的方式来编写此 LCM 函数?
Is there a more clean, elegant, Haskell way to write this LCM function?
我刚开始 Haskell 并敲定了这个简单的递归算法来为列表中的每个数字找到 LCM。它有效,但它很混乱,我希望就如何使它更优雅、更易读和 Haskell-y.
进行一些同行评审
lcms list
| length list > 1 = lcms (lcm (head list) (head (tail list)):(tail (tail list)))
| otherwise = list
因此,它获取一个列表并对前两项进行 LCM,然后将其添加到减去这两个元素的列表中。基本上,我要使用的伪代码是这样的:
lcms [a,b,c] = lcm (a, (lcm (b, c))
任何人有什么建议吗?我渴望在 Haskell 上取得进步,写出人们可以真正阅读的东西。也欢迎效率提示!
谢谢大家!
弃牌:
import Data.List
lcms :: [Int] -> Int
lcms xs = foldl' lcm 1 xs
其中 lcm
计算两个数字的 lcm:
lcm :: Int -> Int -> Int
您几乎可以使用您建议的语法编写它,因此:
lcms (a:b:c) = lcms (lcm a b:c)
lcms list = list
我发现第二个子句有点奇怪,但并不可怕:它可以优雅地处理空列表,尽管 return 当你知道你最多 return 一个列表时可能 return一些 hasocists 认为您的类型有点不精确。您还可以考虑使用 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
我刚开始 Haskell 并敲定了这个简单的递归算法来为列表中的每个数字找到 LCM。它有效,但它很混乱,我希望就如何使它更优雅、更易读和 Haskell-y.
进行一些同行评审lcms list
| length list > 1 = lcms (lcm (head list) (head (tail list)):(tail (tail list)))
| otherwise = list
因此,它获取一个列表并对前两项进行 LCM,然后将其添加到减去这两个元素的列表中。基本上,我要使用的伪代码是这样的:
lcms [a,b,c] = lcm (a, (lcm (b, c))
任何人有什么建议吗?我渴望在 Haskell 上取得进步,写出人们可以真正阅读的东西。也欢迎效率提示!
谢谢大家!
弃牌:
import Data.List
lcms :: [Int] -> Int
lcms xs = foldl' lcm 1 xs
其中 lcm
计算两个数字的 lcm:
lcm :: Int -> Int -> Int
您几乎可以使用您建议的语法编写它,因此:
lcms (a:b:c) = lcms (lcm a b:c)
lcms list = list
我发现第二个子句有点奇怪,但并不可怕:它可以优雅地处理空列表,尽管 return 当你知道你最多 return 一个列表时可能 return一些 hasocists 认为您的类型有点不精确。您还可以考虑使用 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