比较Haskell中的列表元素

时间:2013-12-11 19:02:04

标签: haskell eclipse-fp

我只是在学习Haskell而且有点卡住了。 我想比较列表元素并测量它们之间的差异并返回最高的元素。 不幸的是,我不知道如何解决这个问题。 通常,我只是迭代列表并比较邻居,但这似乎不是Haskell的方式。 我已经尝试过使用map但正如我所说,我真的不知道如何解决这个问题。 我会感谢各种建议!

祝福

编辑:我的想法是首先压缩所有对,如pairs a = zip a (tail a)。然后我想得到所有的差异(可能与map?)然后只选择最高的一个。我只是无法处理Haskell语法。

3 个答案:

答案 0 :(得分:6)

我不知道列表元素之间的“衡量差异”是什么意思,但是如果你想计算列表中的“最大”元素,你就会使用内置的maximum函数:

maximum :: Ord a => [a] -> a

此函数采用可以排序的值列表,因此包括所有数字,字符和字符串等。

如果要获得最大值和最小值之间的差异,可以使用类似的函数minimum,然后只减去这两个函数。当然,可能会有一个稍微快一点的解决方案,你只需要遍历列表一次,或者你可以对列表进行排序然后获取第一个和最后一个元素,但是对于大多数情况来说,diff xs = maximum xs - minimum xs足够快并且最有意义给别人。


所以你想要做的是计算连续元素之间的差异,而不是计算每个元素的最小值和最大值。您不需要直接索引,而是使用名为zipWith的便捷函数。它需要一个二进制操作和两个列表,并使用该二进制操作将它们“拉链”在一起。像

这样的东西
zipWith (+) [1, 2, 3] [4, 5, 6] = [1 + 4, 2 + 5, 3 + 6] = [5, 7, 9]

它非常方便,因为如果其中一个列表提前耗尽,它就会停在那里。所以你可以做类似

的事情
diff xs = zipWith (-) xs ???

但是我们如何将列表偏移1?嗯,简单(和安全)的方法是使用drop 1。您可以使用tail,但如果xs为空列表,则会引发错误并导致程序崩溃,但drop将不会

diff xs = zipWith (-) xs $ drop 1 xs

所以一个例子是

diff [1, 2, 3, 4] = zipWith (-) [1, 2, 3, 4] $ drop 1 [1, 2, 3, 4]
                  = zipWith (-) [1, 2, 3, 4] [2, 3, 4]
                  = [1 - 2, 2 - 3, 3 - 4]
                  = [-1, -1, -1]

此函数将返回正值和负值,我们只对幅度感兴趣,因此我们可以使用abs函数:

maxDiff xs = ??? $ map abs $ diff xs

然后使用我在上面突出显示的功能:

maxDiff xs = maximum $ map abs $ diff xs

你已经完成了!如果你想要花哨,你甚至可以用无点符号来写这个

maxDiff = maximum . map abs . diff

现在,这实际上会在空列表上引发错误,因为maximum []会引发错误,但我会让你找到解决问题的方法。

答案 1 :(得分:3)

如bheklilr所述,maximum是快速简便的解决方案。

如果你想要一些背景,这里有一点。我们要做的是获取值列表并将其减少为单个值。这被称为折叠,并且可以与foldl函数一起使用,其具有签名foldl :: (a -> b -> a) -> a -> [b] -> a

(a -> b -> a)的{​​{1}}部分是一个函数,它接受两个值并返回第一个类型之一。在我们的例子中,这应该是我们的比较函数:

foldl

(请注意,myMax :: Ord a => a -> a -> a myMax x y | x > y = x | otherwise = y 是必需的,以便我们可以比较我们的值。)

所以,我们可以说

Ord a

但是_是什么?拥有此函数的起始值没有意义,因此我们转而使用-- This doesn't work! myMaximum :: Ord a => [a] -> a myMaximum list = foldl myMax _ list ,它不需要起始值(而是从列表中获取前两个值)。这使我们的最大功能

foldl1

或者,以无点格式,

myMaximum :: Ord a => [a] -> a
myMaximum list = foldl1 myMax list

如果您查看myMaximum :: Ord a => [a] -> a myMaximum = foldl1 myMax 中的实际definition of maximum,您会看到它使用相同的方法。

答案 2 :(得分:1)

map将一个函数映射到列表中。它会将列表中的每个thing1转换为thing2

你想要的是找到两个邻居之间的最大区别,而单独使用map是不能做到的。我假设你现在只关注数字,因为这更容易。

diffs :: (Num a) => [a] -> [a]
diffs [] = []
diffs [x] = []
diffs (x1:x2:xs) = abs(x1-x2) : (diffs$x2:xs)

mnd :: (Num a, Ord a) => [a] -> a
mnd [] = 0
mnd [x] = 0
mnd xs = maximum$diffs xs

因此diffs一次获取一个列表项并获得它与其邻居之间的绝对差异,然后将其放在它创建的列表的前面(:运算符将单个元素放在列表的前面。)

mnd只是maximum$diffs xs的一个包装器,可以阻止抛出异常。