basic Haskell : searching through multiple lists for the same elements - haskell

I've been battling with this problem for a while. I'm trying to create an "organisation" which is a list of Gyms. These gyms are a list of People. Each person has an ID number, an age and an amount of credit.
I want the FindID function to search through the organisation to search through the list of Gyms to find the users with the inputted ID and then return the total of their credit. However, I feel I'm overcomplcating the problem and I am really struggling now.
newtype ID = ID Int deriving (Show)
newtype Age = Age Int deriving (Show)
newtype Credit = Credit Int deriving (Show)
newtype Person = Person (ID, Age, Weight) deriving (Show)
type Gym = [Person]
type Organisation = [Gym]
getAge :: Person -> Int
getAge (Person(a,Age b,c)) = b
getID :: Person -> Int
getID (Person(ID a,b,c)) = a
getCredit :: Person -> Int
getCredit (Person(a,b,Credit c)) = c
p = Person ( ID 123, Age 65, Credit 12000)
q = Person ( ID 321, Age 64, Credit 0)
e = Person ( ID 453, Age 30, Credit 3000)
r = Person ( ID 123, Age 65, Credit 2310)
s = Person ( ID 364, Age 32, Credit 32340)
t = Person ( ID 123, Age 65, Credit 1300)
org1 = [p,q,e]
org2 = [r,s,t]
hasPerson :: Gym->Int-> Bool
hasPerson gym' id' = not (null(filter hasperson' gym') )
where
hasperson' person' = getID person' == id'
findID:: ID -> Organisation -> Int
findID id' org = total
where
IsInGym org' = hasPerson ( org' id' )
validGym = filter (IsInGym) org'
total = sum ( map getCredit validGym)

First, I would recommend using a record to represent your person, unless you have a particular reason to assign a new type to each field:
type ID = Int
type Age = Int
type Credit = Int
data Person = Person
{ personId :: ID
, personAge :: Age
, personCredit :: Credit
} deriving (Eq, Show)
type Gym = [Person]
type Organization = [Gym]
Next, you can use map to convert a Gym into [Int] with personId, then you can use the built-in elem to check if the ID given appears in that list.
hasPerson :: Gym -> ID -> Bool
hasPerson gym pId = pId `elem` map personId gym
Now, for the findID function, I would suggest renaming it to something like organizationCredit, and I would make a simpler function called gymCredit to calculate it for a single gym:
gymCredit :: ID -> Gym -> Credit
gymCredit pId gym = sum $ map personCredit $ filter (\p -> personId p == pId) gym
organizationCredit :: ID -> Organization -> Credit
organizationCredit pId org = sum $ map (gymCredit pId) org
Alternatively, you could declare your functions as
gymCredit :: Person -> Gym -> Credit
gymCredit person gym = sum $ map personCredit $ filter (\p -> personId p == pId) gym
where pId = personId person
organizationCredit :: Person -> Organization -> Credit
organizationCredit person org = sum $ map (gymCredit person) org
EDIT: To stick with your old types, you just have to define a few extra functions yourself, then put them in your code where you need to
newtype ID = ID Int deriving (Eq, Show)
newtype Age = Age Int deriving (Eq, Show)
newtype Credit = Credit Int deriving (Eq, Show)
newtype Person = Person (ID, Age, Credit) deriving (Eq, Show)
type Gym = [Person]
type Organisation = [Gym]
personId :: Person -> ID
personId (Person (i, a, c)) = i
personAge :: Person -> Age
personAge (Person (i, a, c)) = a
personCredit :: Person -> Credit
personCredit (Person (i, a, c)) = c
idVal :: ID -> Int
idVal (ID x) = x
ageVal :: Age -> Int
ageVal (Age x) = x
creditVal :: Credit -> Int
creditVal (Credit x) = x
gymCredit :: Person -> Gym -> Credit
gymCredit person gym = Credit $ sum $ map (creditVal . personCredit) $ filter (\p -> personId p == pId) gym
where pId = personId person
organisationCredit :: Person -> Organisation -> Credit
organisationCredit person org = Credit $ sum $ map (creditVal . gymCredit person) org
It is important to note that I've added Eq to the list of derived typeclasses for each newtype. Without it, you wouldn't be able to directly compare two IDs, you'd have to extract the values first. Another important typeclass to derive is Ord, which lets you use the <, >, <=, and >= operators, as well as a whole bunch of list functions like sort.

Related

Getting values from Data in Haskell

type Name = String
type Age = age
data Person = P Name Age derieving (eq)
type People = [Person]
smiths = [P "John" 21, P "Willy" 26]
How to get Johns age? Is there any function like (smiths!!0).age? Or is it only possible with pattern matching?
You can perform pattern matching:
(\(P _ a) -> a) (smiths !! 0)
But it might be better to define Person with record syntax:
data Person = P {
name :: Name
, age :: Age
} deriving Eq
Then Haskell will automatically construct a "getter" age :: Person -> Age, and then you can access this with:
age (smiths !! 0)

Basic Haskell: problems with a function

me again with another basic problem I have. I'm using ghci.
I (with help) created this working code:
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)
isAdult :: Person -> Bool
isAdult (Person(_, Age a, _)) = a > 18
However problems occur when I tried making a more complex function updateWeight that allows the user to change a Person's weight from it's previous value. Can you point out where I have gone wrong?
updateWeight :: Person -> Int -> Person
updateWeight (Person(_,_,Weight w) b = (Person(_,_,w+b))
The problem is that you can't use the _ placeholder on the right hand side of an expression. You'll have to pass through the unchanged values. Also, you must wrap the result of w + b with a Weight again. This should work:
updateWeight :: Person -> Int -> Person
updateWeight (Person(n, a, Weight w) b = (Person(n, a, Weight (w + b)))
You can get rid of the boilerplate of passing through the unchanged values by using record syntax for the Person type.

Basic haskell : Defining types

sorry to bother you.
I'm new to haskell and trying to define a new type Person.
I'm using the GHCI compiler.
I'm running the file new.hs which includes:
Name = String
Age = Int
Weight = Int
Person = (Name, Age, Weight)
but I get not in scope errors. Can anyone help?
Jeremy D helped me with this and I solved that but how can I add a function such as:
isAdult :: Person -> Bool
George = ("George", 20, 80)
isAdult George = if Age >=18
try with:
type Name = String
type Age = Int
type Weigth = Int
type Person = (Name, Age, Weigth)
For a simple introduction, look here
To answer your second question, here is what I did:
newtype Name = Name String deriving (Show)
newtype Age = Age Int deriving (Show)
newtype Weigth = Weight Int deriving (Show)
newtype Person = Person (Name, Age, Weigth) deriving (Show)
isAdult :: Person -> Bool
isAdult (Person(_, Age a, _)) = a > 18
When executing it:
*Main> let p = Person(Name "Jeremy", Age 18, Weight 199)
*Main> p
Person (Name "Jeremy", Age 18, Weight 199)
*Main> isAdult p
False
*Main> let p = Person(Name "Jeremy", Age 20, Weight 199)
*Main> isAdult p
True
*Main>

basic Haskell : list comprehension

Me again, I'm trying to iterate through my list of customers to find the correct customer and when I find them I want to display any non-zero int's that are attached to them. I'm not sure how to proceed. I know there will only be 1 record of the person's name in the shop.
type Name = String
type Customer = (Name,Int,Int)
type Shop = [Customer]
shop = [cust1, cust2]
cust1 = ("Steve", 321, 123) :: Customer
cust2 = ("John", 0,678) :: Customer
getName :: Customer -> Name
getName (a, b,c) = a
getNumbers :: Customer -> [Int]
getNumbers (a,b,c) = filter (/=0) [b,c]
rental:: Shop-> Name -> [Int]
rental shop' name' = map getNumbers [ x|x<-shop',getName x == name']
It is very useful to read error message!
test23.hs:10:9:
Couldn't match type `(Name, t0)' with `(Name, Int, Int)'
Expected type: Customer
Actual type: (Name, t0)
You have
getName (a, b) = a
but is defined
type Customer = (Name,Int,Int)
The right function looks like
getName (a, _, _) = a
After correct, you could see next meassage:
test23.hs:17:26:
Couldn't match type `[Int]' with `Int'
Expected type: Customer -> Int
Actual type: Customer -> [Int]
In the first argument of `map', namely `getNumbers'
...
In an equation for `rental'
But error is not in getNumbers, but in signature of rental:: Shop-> Name -> [Int]. Must be:
rental:: Shop-> Name -> [[Int]]
Your answer is pretty close. First of all, you need to update getName to take a 3-tuple, and second you should use concatMap getNumbers instead of map getNumbers.
Although, it looks like you're going to be adding new fields to your Customer type, so I would recommend that you switch to using a record instead:
data Customer = Customer
{ custName :: Name
, custVal1 :: Int -- I don't know what these are, so use real names
, custVal2 :: Int
} deriving (Eq, Show)
And now you could get rid of getName and do
getNumbers :: Customer -> [Int]
getNumbers c = filter (/= 0) [custVal1 c, custVal2 c]
rental :: Shop -> Name -> [Int]
rental shop' name' = concatMap getNumbers [x | x <- shop', custName x == name']
Now if you were to add another field to Customer, you don't have to update all your functions that don't depend on that field.

something like database but not a database and sum price

How can I sum prices where month = 5 and year = 2010 ? I have date in data Subject and prices in data Sell, they are connected by id. This is my code:
-- subject id, date
data Subject = Subject Int CalendarTime deriving (Read, Show)
-- sell id, subject id, price
data Sell = Sell Int Int Double deriving (Read, Show)
Real world uses should probably use a database or at least a mapping (from the containers or unordered-containers packages), but a simple solution can be obtained using simple list comprehension.
Assuming you have simple lists of Subject and Sell:
type Subjects = [Subject]
type Sells = [Sell]
You could make an O(n*m) implementation (good for play only!):
price :: Sell -> Double
price (Sell _ _ d) = d
calTime :: Subject -> CalendarTime
calTime (Subject _ c) = c
sIdent :: Subject -> Int -- Omitted, you should use record syntax anyway
eIdent :: Sell -> Int -- Omitted
sumPred :: (CalendarTime -> Bool) -> Subjects -> Sells -> Double
sumPred js es = sum [price e | j <- js, e <- es
, sIdent j == eIdent e
, pred (calTime j)]
But as I said, that's foolish. Using a DB with Subjects keyed by CalendarTime and Sell's keyed by identity will give you a more practical solution.

Resources