我刚开始使用 Haskell,想写一个函数,给定一个列表,返回一个列表,其中每个第二个元素都加倍。
到目前为止,我想出了这个:
double_2nd :: [Int] -> [Int]
double_2nd [] = []
double_2nd (x:xs) = x : (2 * head xs) : double_2nd (tail xs)
哪个有效,但我想知道你们将如何编写该函数。有没有更常见/更好的方法,或者这看起来对吗?
我刚开始使用 Haskell,想写一个函数,给定一个列表,返回一个列表,其中每个第二个元素都加倍。
到目前为止,我想出了这个:
double_2nd :: [Int] -> [Int]
double_2nd [] = []
double_2nd (x:xs) = x : (2 * head xs) : double_2nd (tail xs)
哪个有效,但我想知道你们将如何编写该函数。有没有更常见/更好的方法,或者这看起来对吗?
这还不错,以建议的修复为模。一旦您对基础库更加熟悉,您可能会避免显式递归以支持一些更高级别的函数,例如,您可以创建一个函数列表,其中每个函数都存在*2
,并将该函数列表应用(压缩)到您的号码列表:
double = zipWith ($) (cycle [id,(*2)])
您可以通过一些智能模式匹配来避免“空列表”异常。
double2nd (x:y:xs) = x : 2 * y : double2nd xs
double2nd a = a
这只是以下的语法糖
double2nd xss = case xss of
x:y:xs -> x : 2 * y : double2nd xs
a -> a
模式匹配是按顺序完成的,所以xs
会先匹配模式x:y:xs
。然后,如果失败,包罗万象的模式a
将成功。
有点死灵法,但我觉得这个方法对我来说效果很好,想分享一下:
double2nd n = zipWith (*) n (cycle [1,2])
zipWith 采用一个函数,然后将该函数应用于两个列表中的匹配项(第一项到第一项,第二项到第二项等)。函数是乘法,压缩列表是1s和2s的无限循环。zipWith(和所有 zip 变体)停在较短列表的末尾。
在一个奇数长度的列表上试一试:
Prelude> double_2nd [1]
[1,*** Exception: Prelude.head: empty list
你可以看到你的代码的问题。“头”和“尾”从来都不是一个好主意。
对于奇数列表或 double_2nd [x],您可以随时添加
double_2nd (x:xs) | length xs == 0 = [x]
| otherwise = x : (2 * head xs) : double_2nd (tail xs)
谢谢。
这是一个foldr
基于的解决方案。
bar :: Num a => [a] -> [a]
bar xs = foldr (\ x r f g -> f x (r g f))
(\ _ _ -> [])
xs
(:)
((:) . (*2))
测试:
> bar [1..9]
[1,4,3,8,5,12,7,16,9]