我试图找到元组的最小值和最大值。代码在这里:
fFindMinMax :: (Ord a, Ord b, Ord c, Ord d) => [(a, b, c, d)] -> (Double,Double,Double,Double,Double,Double)
fFindMinMax [(_, x, y, z)] = (minimum x, maximum x, minimum y, maximum y, minimum z, maximum z)
错误来自最大x,最大y和最大z。
无法匹配预期类型t1 Double与实际类型b。
有人可以启发我吗?
答案 0 :(得分:3)
首先,您当前的模式:
fFindMinMax [(_, x, y, z)] = ...
表示仅当您使用一个元素传递列表时它才有效。因此,从传递空列表或包含两个或更多元素的列表开始,这将失败。
你需要的是map
来获取元组的所有第二/第三/第四元素。我们可以这样做:
fFindMinMax cs = ...
where cSnd = map (\(_,x,_,_) -> x) cs
cThr = map (\(_,_,y,_) -> y) cs
cFou = map (\(_,_,_,z) -> z) cs
所以现在的问题是:我们应该将什么写为函数谓词。实际上你已经在那里尝试了:我们可以为每个坐标计算minimum cSnd
和maximum cSnd
等等。所以:
fFindMinMax cs = (minimum cSnd, maximum cSnd, minimum cThr, maximum cThr, minimum cFou, maximum cFou)
where cSnd = map (\(_,x,_,_) -> x) cs
cThr = map (\(_,_,y,_) -> y) cs
cFou = map (\(_,_,_,z) -> z) cs
现在该功能的类型是什么?我们不必将我们限制为Double
,也不必确保所有坐标都具有相同的类型,fFindMinMax
的类型签名可以是:
fFindMinMax :: (Ord x, Ord y, Ord z) => [(a, x, y, z)] -> (x, x, y, y, z, z)
fFindMinMax cs = (minimum cSnd, maximum cSnd, minimum cThr, maximum cThr, minimum cFou, maximum cFou)
where cSnd = map (\(_,x,_,_) -> x) cs
cThr = map (\(_,_,y,_) -> y) cs
cFou = map (\(_,_,_,z) -> z) cs
请注意,如果您为其指定一个空列表,则此程序将出错,因为未定义空列表的minimum
或maximum
。
上述解决方案的一个问题是我们使用第二,第三和第四个元素构建列表。由于我们不会同时计算最小值和最大值,这意味着我们最终会将此列表存储到内存中。
我们可以通过编写一个帮助函数来改进代码,该函数同时为同一个列表计算minimum
和maximum
:
{-# LANGUAGE BangPatterns #-}
minmax :: Ord a => [a] -> (a,a)
minmax [] = error "Empty list"
minmax (h:t) = minmax' h h t
where minmax' a b [] = (a,b)
minmax' !a !b (c:cs) = minmax' (min a c) (max b c) cs
这个功能如何运作?那么第一行是相当简单的:如果给出一个空列表,我们无法计算最小值或最大值,因此我们引发错误空列表。在后一种情况下,该列表具有第一项h
和其余项t
。我们认为h
是迄今为止获得的最小值和最大值:如果列表包含单个元素,则该元素既是该列表的最小值也是最大值。因此,我们调用一个额外函数minmax' :: Ord a => a -> a -> [a] -> a
,通过给出迄今为止获得的最小值和最大值(h
和h
)以及列表的剩余部分来计算最小值和最大值。
每次迭代时,它将采用下一个元素c
,并使用迄今为止最小min
和下一个元素a
计算c
的最小值,并计算到目前为止最大b
和下一个元素c
的最大值。我们一直在迭代这样做,直到我们到达列表的末尾。在这种情况下,迄今为止获得的最小值和最大值是最终的最小值和最大值。所以我们可以将它们作为(a,b)
返回。
然后我们可以使用如下函数:
fFindMinMax :: (Ord x, Ord y, Ord z) => [(a, x, y, z)] -> (x, x, y, y, z, z)
fFindMinMax cs = (xmi, xma, ymi, yma, zmi, zma)
where (xmi, xma) = minmax $ map (\(_,x,_,_) -> x) cs
(ymi, yma) = minmax $ map (\(_,_,y,_) -> y) cs
(zmi, zma) = minmax $ map (\(_,_,_,z) -> z) cs
答案 1 :(得分:1)
fFindMinMax :: (Ord x, Ord y, Ord z) => [(a, x, y, z)] -> (x, x, y, y, z, z)
fFindMinMax ((_, x, y, z):rest) = foldl' minmax (x, x, y, y, z, z) rest
fFindMinMax [] = error "fFindMinMax: empty list"
where
(xMin', xMax', yMin', yMax', zMin', zMax') `minmax` (_, x, y, z) =
let
!xMin = min xMin' x
!xMax = max xMax' x
!yMin = min yMin' y
!yMax = max yMax' y
!zMin = min zMin' z
!zMax = max zMax' z
in (xMin, xMax, yMin, yMax, zMin, zMax)