2017-10-07 44 views
1

我有個問題,我必須做的程序以總結的表,例如:表總結的Haskell

性別身高

女120

男160

女180

並且結果必須是,平均值如下:

女150

男160

到目前爲止,我得到這個:

altura :: [([Char], Integer)] -> Integer 
altura [] = 1 
altura ((gender,height): x) = if gender == "Female" 
then height + (altura x) 
else if gender == "Male" 
then height + (altura x) 
else 0 

我不知道怎麼弄的性別和分工。 謝謝先進。

回答

1

當您沿着列表行進時,分別跟蹤每個性別的總計身高和數量(人口),然後在列表末尾將總計身高除以每個性別的計數以得到平均身高每個性別:

import Data.Ratio ((%)) 

averages :: [(String, Integer)] -> (Rational, Rational) 
averages = go 0 0 0 0 
    where 
    rat :: Integer -> Integer -> Rational 
    rat 0 0 = 0 
    rat n d = n % d 

    go :: Integer -> Integer -> Integer -> Integer -> [(String, Integer)] -> (Rational, Rational) 
    go fHeight fCount mHeight mCount [] = (rat fHeight fCount, rat mHeight mCount) 
    go fHeight fCount mHeight mCount (("Female", height):xs) = go (fHeight + height) (fCount + 1) mHeight mCount xs 
    go fHeight fCount mHeight mCount (("Male", height):xs) = go fHeight fCount (mHeight + height) (mCount + 1) xs 

這給:

> averages [("Female", 120), ("Male", 160), ("Female", 180)] 
(150 % 1,160 % 1) 
1

你可以打破這個問題分爲兩個部分:

  • 首先將成對列表[(a, b)]分組到成對列表[(a, [b])]中,其中具有相同a的所有(a, b)已被收集在一起。

  • 然後計算平均值爲整數的每個列表

這給我們:

module Averages where 

import Data.Function 
import Data.Ratio 
import Data.List 

classes :: Ord a => [(a, b)] -> [(a, [b])] 
classes = fmap ((,) <$> fst . head <*> fmap snd) 
     . groupBy ((==) `on` fst) 
     . sortBy (compare `on` fst) 

average :: [Integer] -> Rational 
average xs = sum xs % genericLength xs 

averages :: Ord a => [(a, Integer)] -> [(a, Rational)] 
averages = fmap (fmap average) . classes 

classes您需要首先對列表進行排序,因爲groupBy只向組相鄰值加在一起。