我必须创建一个称为差异的函数,在该函数中,我用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]
。
答案 0 :(得分:3)
您快到了-但是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
是一个可行的选择,并且稍微麻烦一些。
答案 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
,init
和tail
的拼写:
drop