Alex
Alex

Reputation: 667

basic haskell: finding an average between two values

newtype Name = Name String deriving (Show)
newtype Age = Age Int deriving (Show)
newtype Weight = Weight Int deriving (Show)
newtype Person = Person (Name, Age, Weight) deriving (Show)   
type Gym = [Person]

isAdult :: Person -> Bool
isAdult (Person (_, Age a, _)) = a > 18

w = Person (Name "Lee", Age 30, Weight 120)

p = Person (Name "John" , Age 65, Weight 80)

updateWeight :: Person -> Int -> Person
updateWeight (Person (n, a, Weight w)) b = Person (n,  a, Weight $ w + b)

getWeight :: Person -> Int
getWeight (Person (a, b, Weight c)) = c

getAge :: Person -> Int
getAge (Person (a, Age b, c)) = b

I was now thinking of trying find the average weight of people in the Gym between two ages.

So far I've got

getAverage:: Gym -> (Int, Int) -> Int
getAverage a (b,c) = sum . map getWeight . filter ((b >= age && c <= age) || (b <= age && c >= age))
    where
        age = getAge a

Upvotes: 1

Views: 190

Answers (1)

bheklilr
bheklilr

Reputation: 54058

What you have is close, but not quite there. I would recommend breaking down the problem into sub-expressions and use a where or let local binding to make it more clear what you want to do:

getAverage :: Gym -> (Int, Int) -> Double
getAverage gym (minAge, maxAge) = fromIntegral total / fromIntegral count
    where
        isInAgeRange person = minAge <= age && age <= maxAge where age = getAge person
        validPeople = filter isInAgeRange gym
        total = sum $ map getWeight validPeople
        count = length validPeople

From this point on, you could combine some of these lines to shorten the code, but this is incredibly readable, it won't suffer from many efficiency problem (although you can calculate the sum and the count at the same time with a fancy fold), and it doesn't take too many lines to write as it is.

Upvotes: 2

Related Questions