我只是在学习 Haskell,有点卡住了。我想比较列表元素并测量它们之间的差异并返回最高的元素。不幸的是,我不知道如何解决这个问题。通常,我只是迭代列表并比较邻居,但这似乎不是 Haskell 的方式。我已经尝试过使用map
,但正如我所说,我真的不知道如何解决这个问题。我会感谢各种建议!
最好的祝愿
编辑:我的想法是首先像这样压缩所有对pairs a = zip a (tail a)
。然后我想获得所有差异(也许有map
?),然后选择最高的。我只是无法处理 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 []
会引发错误,但我会让你想办法解决这个问题。
正如 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
,您会发现它使用了相同的方法。
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
停止抛出异常的包装器。