Algorithm Haskell递归-查找列表中数字之间的最大差异
现在的问题是:我需要使用递归找到列表中相邻数字之间的最大差异。以下面的列表为例:[1,2,5,6,7,9]。两个相邻数字之间的最大差值为3(介于2和5之间) 我知道递归可能不是最好的解决方案,但我正在努力提高在Haskell中使用递归的能力 以下是我当前拥有的代码:Algorithm Haskell递归-查找列表中数字之间的最大差异,algorithm,haskell,recursion,Algorithm,Haskell,Recursion,现在的问题是:我需要使用递归找到列表中相邻数字之间的最大差异。以下面的列表为例:[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(即,无法比
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))
再次感谢大家 因此,代码抛出错误的原因是
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
。仅供参考,diff
实际上只是一个计算数字列表导数的函数,对于数据处理来说,它可能是一个非常有用的函数
编辑:需要对
largestDiff
进行Ord
约束,并在计算最大值之前添加到map abs
。因此,代码抛出错误的原因是
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
。仅供参考,diff
实际上只是一个计算数字列表导数的函数,对于数据处理来说,它可能是一个非常有用的函数
编辑:需要对largestDiff
进行Ord
约束,并在计算最大值之前添加到map abs
。以下是我的看法
首先是一些助手:
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))
您必须提供两个结束子句,因为序列可能包含偶数或奇数个元素。我的看法是这样的
首先是一些助手:
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))
您必须提供两个结束子句,因为序列可能包含偶数或奇数个元素。可以获得此问题的另一个解决方案,它可以避免您的错误 只需变换列表并折叠/缩小它们
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
的第二个列表是drop1x
,这只是另一种方法
描述列表的尾部,但是tail[]
会导致错误,
drop 1[]
只生成空列表。因此drop 1
是“更安全”的选择
因此,第一个函数计算相邻的差值
第二个函数的名称表明它计算最大绝对值
给定列表的值(仅部分为真),如果传递
空列表
但这是如何发生的,从右到左阅读,我们看到map abs
将每个列表元素转换为其绝对值,该值由
Num a
约束。然后foldl'
-函数遍历列表并
累加上一个累加器和当前元素的最大值
列表遍历。此外,我想指出,foldl'
是“严格的”
foldl
-函数的姐妹/兄弟,后者很少使用,
因为它倾向于建立一组称为thunks的未赋值表达式
所以,让我们停止这些废话,看看它的实际行动;-)
>设a=diff[1..3]:[Int]
>>>zipWith(-)[1,2,3](下降1[1,2,3])
zipWith(-)[1,2,3][2,3]
[1-2,2-3]——zipWith在en处停止