问题
I'm trying to calculate the total balance of people/clients between two ages that have an account within a bank. I can get it to display the list of people that fall under the requirement.
These are the constructors
type NI = Int
type Age = Int
type Balance = Int
type Person = (NI, Age, Balance)
type Bank = [Person]
This is the Bank
rbs :: Bank
rbs = [ (1, 73, 1000)
, (2, 18, -50)
, (3, 60, 190)
, (4, 26, 300)
, (5, 24, 456)
, (6, 32, 7500)
, (7, 41, -46)
, (8, 59, -850)
, (9, 44, 348)
, (10, 66, -1000)
, (11, 37, 20000)
, (12, 29, -245)
, (13, 55, 3090)
]
And this is my code to recursively check the bank
equityA' :: Bank -> (Int, Int) -> Bank
equityA' ((n,a,b):xs) (0,0) = error "No ages were selected"
equityA' [] (x,y) = []
equityA' ((n,a,b):xs) (f, s) = if (f <= a) && (s >= a) then (n,a,b) : equityA' xs (f, s)
else equityA' xs (f, s)
If I run equityA' rbs (40,50) the output will be [(7,41,-46),(9,44,348)]
What i'm struggling to do is to is print out the total balance of those people. I have some code down but am stuck at the actual calculation part.
The code for to check the total.
equityAge :: Bank -> (Int, Int) -> Int
equityAge ((n,a,b):xs) (0,0) = error "No ages were selected"
equityAge [] (x,y) = 0
equityAge ((n,a,b):xs) (f, s) =
I would be grateful for any help.
回答1:
It's quite simple if you use list comprehensions:
equityAge :: Bank -> (Int,Int) -> Int
equityAge _ (0,0) = error "No ages were selected"
equityAge [] (x,y) = 0
equityAge x (f,s) = sum [ b | (n,a,b) <-x, f <= a, a <= s]
Other than that you can apply Sarah's advice and use map
and sum
on your original function:
equityAge :: Bank -> (Int,Int) -> Int
equityAge _ (0,0) = error "No ages were selected"
equityAge [] (x,y) = 0
equityAge x (f,s) = sum (map(\(_,_,b) -> b) (equityA' x (f,s)))
回答2:
There are three main things I would recommend here: (a) use record types instead of tuples; (b) use standard utility functions to work with lists and other standard types; (c) write auxiliary functions to help keep all of you functions short and readable.
On the first recommendation:
data Person = Person { ni :: NI, age :: Age, balance :: Balance } deriving (Eq, Show)
Now you don't have to do any of that tuple pattern matching just to get a person's age; this record type declaration gets you functions age :: Person -> Age
and balance :: Person -> Balance
for free.
Example of the third recommendation: here is a function to calculate whether a Person
is between two ages:
betweenAges :: Age -> Age -> Person -> Bool
betweenAges lo hi person = lo <= age person && age person <= hi
Now, using that auxiliary function and the standard filter
function, you can get all the persons between two ages like this:
filter (betweenAges 18 27) rbs
To get the balance of a list of Person
s you can just use the standard sum
and map
functions:
sum (map balance somePersons)
So the complete solution would be something like this:
-- Note how much easier to read this is when you use sum, map, filter
-- and betweenAges!
equityAge :: Age -> Age -> Bank -> Balance
equityAge lo hi bank = sum (map balance (filter (betweenAges lo hi) bank))
-- This could be its own top-level function if you're going to reuse it
-- somewhere else, but here I'm putting it in a where declaration assuming
-- that it's a throwaway.
where betweenAges lo hi person = lo <= age person && age person <= hi
As a general rule, laboring away to figure out how to solve the issue you're tackling is not productive. The more productive strategy, I believe, is the following: (a) learn to solve your problems in terms of the functions in the basic libraries like Prelude
, Data.List
and Data.Maybe
; (b) learn how to write those utility functions on your own. Why this way? Because (a) teaches you how to break problems down into small, reusable pieces, and (b) teaches you to understand what's going on from the bottom to the top in these programs.
来源:https://stackoverflow.com/questions/13333607/sum-the-total-balance-for-people-between-two-ages