比较Haskell中的列表元素



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

向致以最良好的祝愿

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

我不知道你所说的"测量列表元素之间的差异"是什么意思,但如果你想计算列表中的"最大"元素,你可以使用内置的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 []抛出了一个错误。但我会让您找到解决这个问题的方法。

正如bhekliler所提到的,maximum是一个快速而简单的解决方案。

如果你想要一些背景,这里有一些。我们试图做的是获取一个值列表,并将其简化为一个值。这被称为折叠,并且可以使用(除其他外)具有签名foldl :: (a -> b -> a) -> a -> [b] -> afoldl函数。

foldl(a -> b -> a)部分是一个取两个值并返回第一种类型之一的函数。在我们的情况下,这应该是我们的比较函数:

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

如果你看看Data.List中最大值的实际定义,你会发现它使用了同样的方法。

map将函数映射到列表上。它将列表中的每个CCD_ 23转换为一个CCD_。

你想要的是找到两个邻居之间最大的差异,这是仅靠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的一个包装器,用于阻止抛出异常。

最新更新