这就是手头的问题:我需要使用递归找到列表中相邻数字之间的最大差异。以下面的列表为例:[1,2,5,6,7,9]。两个相邻数字之间的最大差异是3(2到5之间)。
我知道递归可能不是最佳解决方案,但我正在努力提高我在Haskell中使用递归的能力。
这是我目前拥有的当前代码:
largestDiff (x:y:xs) = if (length (y:xs) > 1) then max((x-y), largestDiff (y:xs)) else 0
基本上 - 列表将一直变短,直到达到1(即不能再比较数字,然后返回0)。当0向上调用调用堆栈时,max函数随后用于实现“山丘之王”类型算法。最后 - 在调用堆栈的末尾,应返回最大的数字。
麻烦的是,我的代码中出现了一个我无法解决的错误:
Occurs check: cannot construct the infinite type:
t1 = (t0, t1) -> (t0, t1)
In the return type of a call of `largestDiff'
Probable cause: `largestDiff' is applied to too few arguments
In the expression: largestDiff (y : xs)
In the first argument of `max', namely
`((x - y), largestDiff (y : xs))'
任何人都有一些智慧的话语可以分享?
谢谢你的时间!
编辑:感谢大家的时间 - 经过多次试验和错误,我最终独立地发现了一种更为简单的方法。
largestDiff [] = error "List too small"
largestDiff [x] = error "List too small"
largestDiff [x,y] = abs(x-y)
largestDiff (x:y:xs) = max(abs(x-y)) (largestDiff (y:xs))
再次感谢,全部!
答案 0 :(得分:3)
因此,您的代码抛出错误的原因是因为
max((x-y), largestDiff (y:xs))
在Haskell中,你不要在参数周围使用括号并用逗号分隔它们,正确的语法是
max (x - y) (largestDiff (y:xs))
您使用的语法被解析为
max ((x - y), largestDiff (y:xs))
看起来你正在将一个元组传递给max
!
然而,这并没有解决问题。我总是回到0
。相反,我建议将问题分解为两个功能。你想计算差值的最大值,所以先写一个函数来计算差值,然后用一个函数来计算它们的最大值:
diffs :: Num a => [a] -> [a]
diffs [] = [] -- No elements case
diffs [x] = [] -- One element case
diffs (x:y:xs) = y - x : diffs (y:xs) -- Two or more elements case
largestDiff :: (Ord a, Num a) => [a] -> a
largestDiff xs = maximum $ map abs $ diffs xs
请注意我是如何将递归拉到最简单的情况下的。当我们遍历列表时,我们不需要计算最大值;它可能,更复杂。由于Haskell有一个方便的内置函数来计算我们的列表最大值,我们也可以利用它。我们的递归函数简洁明了,然后与maximum
结合使用以实现所需的largestDiff
。作为一个FYI,diffs
实际上只是计算数字列表的导数的函数,它可以是一个非常有用的数据处理函数。
编辑:Ord
上需要largestDiff
约束,并在计算最大值之前添加到map abs
。
答案 1 :(得分:1)
这是我的看法。
首先是一些助手:
diff a b = abs(a-b)
pick a b = if a > b then a else b
然后是解决方案:
mdiff :: [Int] -> Int
mdiff [] = 0
mdiff [_] = 0
mdiff (a:b:xs) = pick (diff a b) (mdiff (b:xs))
您必须提供两个结束子句,因为序列可能包含偶数或奇数个元素。
答案 2 :(得分:1)
可以获得解决此问题的另一个解决方案 只需转换列表并折叠/缩小它们。
import Data.List (foldl')
diffs :: (Num a) => [a] -> [a]
diffs x = zipWith (-) x (drop 1 x)
absMax :: (Ord a, Num a) => [a] -> a
absMax x = foldl' max (fromInteger 0) (map abs x)
现在我承认这对于初学者来说有点密集,所以我将解释上面的内容。
函数zipWith
使用二元函数转换两个给定列表,
在这种情况下,(-)
。
我们传递给zipWith
的第二个列表是drop 1 x
,这只是另一种方式
描述列表的尾部,但tail []
导致错误,
drop 1 []
只生成空列表。所以drop 1
是“更安全”的选择。
所以第一个函数计算相邻的差异。
第二个函数的名称表明它计算最大绝对值 给定列表的值,只是部分为真,如果传递给它,则结果为“0” 空列表。
但是这是如何发生的,从右到左阅读,我们看到map abs
将每个列表元素转换为其绝对值,由...声明
Num a
约束。然后foldl'
- 函数遍历列表并且
累加前一个累加器和当前元素的最大值
列表遍历。此外,我想提一下foldl'
是“严格的”
foldl
- 函数的姐妹/兄弟,后者很少使用,
因为它倾向于建立一堆被称为thunks的未评估表达式。
所以,让我们放弃所有这些等等,看看它的实际效果; - )
> let a = diffs [1..3] :: [Int]
>>> zipWith (-) [1,2,3] (drop 1 [1,2,3])
<=> zipWith (-) [1,2,3] [2,3]
<=> [1-2,2-3] -- zipWith stops at the end of the SHORTER list
<=> [-1,-1]
> b = absMax a
>>> foldl' max (fromInteger 0) (map abs [-1,-1])
-- fromInteger 0 is in this case is just 0 - interesting stuff only happens
-- for other numerical types
<=> foldl' max 0 (map abs [-1,-1])
<=> foldl' max 0 [1,1]
<=> foldl' max (max 0 1) [1]
<=> foldl' max 1 [1]
<=> foldl' max (max 1 1) []
<=> foldl' max 1 [] -- foldl' _ acc [] returns just the accumulator
<=> 1