我必须创建一个名为差异的函数,在那里我用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)]
然后像这样使用list comprehension:
[zipWith (-) a b | a <- y, b <- x]
其中x是元组的第一个元素,y是第二个元素。
函数类型是differences :: Num a => [a] -> [a]
。
你几乎就在那里 - 但是zipWith
本身会返回一个列表,所以你不想把它放在列表解析中,除非你希望结果是列表列表(你不在这里)。
zipWith (-)
绝对是正确的想法 - 它需要2个列表,并通过获取给定列表的相应元素之间的差异给出一个新列表。在您的情况下,您的输出列表比一个输入列表短1个元素,并且您希望在2个列表中使用zipWith (-)
,其中包括:
Haskell已经为我们提供了方便的功能,即tail和init。
所以你要找的功能是:
differences xs = zipWith (-) (tail xs) (init xs)
请注意,这并不理想,因为如果init
为空,tail
和xs
都会以一个丑陋的运行时错误使程序崩溃。如果你向这个函数提供一个空列表,输出一个空列表是有意义的(虽然你可以认为它没有,因为你将从单例列表中得到一个空列表),所以你可以通过定义一个空列表来避免运行时崩溃通过模式匹配来明确地满足空列表:
differences [] = []
differences xs = zipWith (-) (tail xs) (init xs)
虽然我个人认为这很好,而且很明确,你实际上并不需要同时使用init
和tail
- zipWith
工作得很好,如果出现不等长度的列表,当它只是将较大的一个缩小到大小。所以differences xs = zipWith (-) (tail xs) xs
是一个可行的,稍微有点替代品。
建立Robin Zigmond's answer,功能的Applicative
实例在这里运作良好:
(f <*> g) xs == f xs (g xs)
所以
differences = zipWith subtract <*> tail
(其中subtract = flip (-)
。)
As 4castle suggests,在drop 1
中使用tail
而不是Robin Zigmond's second, init
-less solution意味着我们可以省略[]
案例,如drop 1 [] = []
(与tail []
不同,这会导致运行时错误):
differences xs = zipWith (-) (drop 1 xs) xs
为了与没有明确模式解构的解决方案形成对比,我将提到使用init
,tail
和drop
的拼写:
differences xs@(_:ys) = zipWith (-) ys xs