2017-08-28 212 views
2

我試圖找到元組的最小值和最大值。代碼如下:Haskell ::查找一組元組的最小值和最大值

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。

Couldnt match expected type t1 Double with actual type b.

有人能給我啓發嗎?

+0

您打算變出你的六個雙打出了什麼事情?爲什麼六個而不是四十二個? –

+0

它是一組座標(id,x,y,z)。因此,我想提取每個x,y和z的最小值和最大值,並將其值設置爲6. –

+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 cSndmaximum 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

請注意,如果你給它這個程序會出錯一個空列表,因爲沒有定義空列表的minimummaximum

改善內存消耗

與上述解決方案的問題是,我們構造列表與所述第二,第三和第四元件。由於我們沒有同時計算最小值和最大值,這意味着我們最終會將這些列表存儲到內存中。

我們可以通過編寫計算minimummaximum同時對同一列表中的輔助函數提高代碼:

{-# 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,通過給它最小值和最大值(hh)和剩下的列表來計算最小值和最大值。

每次迭代時,將採取的下一個元素c並且與迄今最小a和下一個元素c計算最小通過計算min,且與迄今最大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
+0

是的!就是這個。我喜歡你解釋它的方式。根據流程顯示的步驟。謝謝!!! –

+0

值得一提的是,這個任務可以解決恆定的內存空間(與此相反)。 – freestyle

+0

您可以詳細說明(如您之前所做的)您如何定義函數minmax?我很難消化它。抱歉。 我試圖編譯它,但最大b h的錯誤。 –

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)