我只是在学习Haskell而且有点卡住了。
我想比较列表元素并测量它们之间的差异并返回最高的元素。
不幸的是,我不知道如何解决这个问题。
通常,我只是迭代列表并比较邻居,但这似乎不是Haskell的方式。
我已经尝试过使用map
但正如我所说,我真的不知道如何解决这个问题。
我会感谢各种建议!
祝福
编辑:我的想法是首先压缩所有对,如pairs a = zip a (tail a)
。然后我想得到所有的差异(可能与map
?)然后只选择最高的一个。我只是无法处理Haskell语法。
答案 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
的一个包装器,可以阻止抛出异常。