Haskell:列表中的每个第2个元素加倍

use*_*087 19 haskell

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

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

double_2nd :: [Int] -> [Int]
double_2nd [] = []
double_2nd (x:xs) = x : (2 * head xs) : double_2nd (tail xs)
Run Code Online (Sandbox Code Playgroud)

哪个有效,但我想知道你们怎么会写这个功能.是否有更常见/更好的方式或看起来对吗?

Tho*_*son 43

这还不错,以修正建议为模.一旦你对基础库更加熟悉,你可能会避免使用显式递归来支持某些更高级别的函数,例如,你可以创建一个函数列表,其中每个函数列表都是,*2并将这些函数列表应用(zip)给你的数字列表:

double = zipWith ($) (cycle [id,(*2)])
Run Code Online (Sandbox Code Playgroud)

  • 是否有理由比"double = zipWith(*)(cycle [1,2])"更喜欢这个?对我来说似乎更直截了当. (9认同)
  • @Ankur如果必须,并行列表理解更清晰,因为你可以避免构造和破坏元组.`double lst = [fx | x < - lst | f < - cycle [id,(*2)]]` (6认同)

cdk*_*cdk 20

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

double2nd (x:y:xs) = x : 2 * y : double2nd xs
double2nd a = a
Run Code Online (Sandbox Code Playgroud)

这只是以下语法糖

double2nd xss = case xss of
    x:y:xs -> x : 2 * y : double2nd xs
    a -> a
Run Code Online (Sandbox Code Playgroud)

模式匹配按顺序完成,因此xs将首先与模式匹配x:y:xs.然后,如果失败,catch-all模式a将成功.

  • @mariosangiorgio:模式匹配的标准解释是"从上到下,从左到右".您可以进行一些重新排序,只要您做出与标准解释相同的选择. (3认同)
  • @mariosangiorgio:构造函数可以有多个字段(例如`(:)`有两个字段).从左到右指的是构造函数的字段,从上到下指的是替代列表. (2认同)

ste*_*ess 10

有点神秘,但我认为这种方法对我来说非常好,并希望分享:

double2nd n = zipWith (*) n (cycle [1,2])
Run Code Online (Sandbox Code Playgroud)

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

  • 你可以通过删除两边的`n`来减少这个,即`double2nd = zipWith(*)(cycle [1,2])` (3认同)
  • 试试看吧!我们提供带有前2个参数的`zipWith`(即我们部分应用它),它创建了一个仍然需要最后一个参数的函数.然后我们将该函数命名为"double2nd",我们就完成了.在这种情况下,无需为参数命名.这种简化称为eta-reduction.我们消除的论证必须是最后一个,但我们可以在这种情况下交换`n`和`cycle`,因为我们正在相乘,所以顺序并不重要. (3认同)

Chr*_*icz 5

在奇数长度列表上尝试:

Prelude> double_2nd [1]
[1,*** Exception: Prelude.head: empty list
Run Code Online (Sandbox Code Playgroud)

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