20

我刚开始使用 Haskell,想写一个函数,给定一个列表,返回一个列表,其中每个第二个元素都加倍。

到目前为止,我想出了这个:

double_2nd :: [Int] -> [Int]
double_2nd [] = []
double_2nd (x:xs) = x : (2 * head xs) : double_2nd (tail xs)

哪个有效,但我想知道你们将如何编写该函数。有没有更常见/更好的方法,或者这看起来对吗?

4

6 回答 6

43

这还不错,以建议的修复为模。一旦您对基础库更加熟悉,您可能会避免显式递归以支持一些更高级别的函数,例如,您可以创建一个函数列表,其中每个函数都存在*2,并将该函数列表应用(压缩)到您的号码列表:

double = zipWith ($) (cycle [id,(*2)])
于 2013-06-29T18:20:49.667 回答
22

您可以通过一些智能模式匹配来避免“空列表”异常。

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将成功。

于 2013-06-29T18:08:52.483 回答
10

有点死灵法,但我觉得这个方法对我来说效果很好,想分享一下:

double2nd n = zipWith (*) n (cycle [1,2])

zipWith 采用一个函数,然后将该函数应用于两个列表中的匹配项(第一项到第一项,第二项到第二项等)。函数是乘法,压缩列表是1s和2s的无限循环。zipWith(和所有 zip 变体)停在较短列表的末尾。

于 2014-08-01T12:05:07.963 回答
5

在一个奇数长度的列表上试一试:

Prelude> double_2nd [1]
[1,*** Exception: Prelude.head: empty list

你可以看到你的代码的问题。“头”和“尾”从来都不是一个好主意。

于 2013-06-29T18:05:02.957 回答
0

对于奇数列表或 double_2nd [x],您可以随时添加

    double_2nd (x:xs) | length xs == 0 = [x] 
                      | otherwise = x : (2 * head xs) : double_2nd (tail xs)

谢谢。

于 2019-02-21T16:15:23.953 回答
0

这是一个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]
于 2019-10-10T16:17:41.617 回答