使用zipWith计算列表元素的差异

时间:2019-04-20 22:55:02

标签: list haskell tuples

我必须创建一个称为差异的函数,在该函数中,我用zipWith计算每对的差异并将其放在列表中。

例如differences [1..5] == [1, 1, 1, 1]

所以[2-1, 3-2, 4-3, 5-4] == [1, 1, 1, 1]

我想到要列出元组,像这样:

[1..5] = [(1,2), (2,3), (3,4), (4,5)]

然后使用像这样的列表组合:

[zipWith (-) a b | a <- y, b <- x]

其中x是元组的第一个元素,y是第二个元素。

函数类型为differences :: Num a => [a] -> [a]

3 个答案:

答案 0 :(得分:3)

您快到了-但是zipWith本身会返回一个列表,因此,除非您希望结果是列表列表,否则您不希望将其放入列表理解中。 ,这里)。

zipWith (-)在这里绝对是正确的主意-它有2个列表,并通过考虑给定列表中相应元素之间的差异给出了一个新列表。在您的情况下,您的输出列表打算比一个输入列表短1个元素,并且您想在2个包含以下内容的列表上使用zipWith (-)

  • 给定列表中的所有元素(第一个元素除外)
  • 给定列表中的所有元素,除了最后一个

Haskell已经为我们提供了方便的功能,即tailinit

所以您要寻找的功能是:

differences xs = zipWith (-) (tail xs) (init xs)

请注意,这不是理想的选择,因为如果init为空,tailxs都将使程序崩溃并出现难看的运行时错误。如果向此函数显示一个空列表,则输出一个空列表是有意义的(尽管您可能会辩称不这样做,因为您将从单例列表中获得一个空列表),因此可以通过定义通过模式匹配功能来显式满足空列表的需求:

differences [] = []
differences xs = zipWith (-) (tail xs) (init xs)

虽然我个人认为这很好,也很明确,但是实际上您不需要同时使用inittail-zipWith可以很好地工作,如果出现不相等的列表长度时,只需将较大的一个修剪成小尺寸即可。因此,differences xs = zipWith (-) (tail xs) xs是一个可行的选择,并且稍微麻烦一些。

答案 1 :(得分:2)

Robin Zigmond's answer为基础,函数的Applicative实例在这里运作良好:

(f <*> g) xs == f xs (g xs)

如此

differences = zipWith subtract <*> tail

(其中subtract = flip (-)。)

答案 2 :(得分:0)

As 4castle suggests,在Robin Zigmond's second, init-less solution中使用drop 1代替tail意味着我们可以省略[]的情况,就像drop 1 [] = [](不同于{{ 1}},这会导致运行时错误):

tail []

与没有显式模式解构的该解决方案形成对比,我将提到不使用differences xs = zipWith (-) (drop 1 xs) xs inittail的拼写:

drop