1

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

最好的祝愿

编辑:我的想法是首先像这样压缩所有对pairs a = zip a (tail a)。然后我想获得所有差异(也许有map?),然后选择最高的。我只是无法处理 Haskell 语法。

4

3 回答 3

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 []会引发错误,但我会让你想办法解决这个问题。

于 2013-12-11T19:06:36.557 回答
3

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

如果你想要一些背景知识,这里有一点。我们正在尝试做的是获取一个值列表并将其减少为单个值。这被称为折叠,并且(除其他外)foldl具有签名的函数是可能的foldl :: (a -> b -> a) -> a -> [b] -> a

(a -> b -> a)部分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

如果您查看max in的实际定义Data.List,您会发现它使用了相同的方法。

于 2013-12-11T19:31:26.303 回答
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停止抛出异常的包装器。

于 2013-12-11T21:50:17.710 回答