Haskell Programming - list

I need to implement a function that takes a list of Dists and returns a list of Dists. I need the function to return only the Dists with the label "pass", but somehow this does not work. Any help?
data Ex = Ex Float Float String String deriving Show
data NewSt = NewSt Float Float String deriving Show
data Dist = Dist Float NewSt Ex deriving Show
helper1 [] = []
helper1 (Dist x (NewSt midterm1 quiz1 name1) (Ex midterm quiz name label) : xs) = if (label == "pass")
then Dist x (NewSt midterm1 quiz1 name1) (Ex midterm quiz name label) : (helper1 xs)
else helper1 xs

This is a little simpler to write with more pattern matching than with an if expression.
helper1 :: [Dist] -> [Dist]
helper1 [] = []
helper1 (Dist x newst (Ex midterm quiz name "pass") : xs) = Dist x newst (Ex midterm quiz name "pass") : (helper1 xs)
helper (_:xs) = helper1 xs
However, it is even simpler once you recognize that your recursion is already implemented by the filter function.
helper1 :: [Dist] -> [Dist]
helper1 = filter passed
where passed (Dist _ _ (Ex _ _ _ "pass")) = True
passed _ = False

I guess you may do as follows;
data Ex = Ex Float Float String String deriving Show
data NewSt = NewSt Float Float String deriving Show
data Dist = Dist Float NewSt Ex deriving Show
myData :: [Dist]
myData = [Dist 1 (NewSt 66 100 "John") (Ex 55 90 "John" "pass"),
Dist 2 (NewSt 20 45 "Tom") (Ex 33 50 "Tom" "fail"),
Dist 3 (NewSt 75 75 "Mary") (Ex 90 100 "Mary" "pass")]
helper1 [] = []
helper1 d#(Dist _ _ (Ex _ _ _ label):ds) | label == "pass" = (head d):(helper1 ds)
| otherwise = helper1 ds

Related

Calculate Price of items in a list

I am learning algebraic-data-types in Haskell, and I ran into a problem I cannot solve.
So I want to enter a list of groceries, and as an output I should get the total price.
For now, I have the types:
data Item = A | B | C deriving (Eq, Show)
data List = Empty | Add Item List
and I have the groceries list: list1 = A `Add` (B `Add` Empty)
I have a function (That sets the prices of the items):
p:: Item -> Int
p A = 2
p B = 4
p C = 5
And now here is where I have a difficulty.
I want to apply the function "p" to my list1. But I am not sure how.
Here is my current code:
price:: (Item -> Int) -> List -> Int
price p (Add x xs)
| x == A = A + price xs
| x == B = B + price xs
| x == C = C + price xs
| otherwise = 0
I also tried this.
price:: (Item -> Int) -> List -> Int
price p (Add x xs) = x + price xs
Thanks in advance!
price's first parameter is a function which accepts List parameter and returns Int (like the declared p function, but do not confuse it with p parameter in price p (Add x xs) pattern, I renamed the former one for clarity to c), so the implementation can look like the following:
price :: (Item -> Int) -> List -> Int
price _ Empty = 0 -- match empty List
price c (Add x xs) = (c x) + price c xs -- match non-empty and perform recursive call to price
With call looking like price p $ Add A Empty (calculates price of list of one A).
In addition to chepner's suggestion to implement mapList I might suggest writing foldrList.
foldrList _ i Empty = i
foldrList f i (Add x xs) = f x (foldrList f i xs)
Having done so, the total price of a list of items can be calculated:
totalPrice items = foldrList (\item acc -> p item + acc) 0 items
Or simply:
totalPrice = foldrList (\item acc -> p item + acc) 0

How to compute the average of a list of a special record in OCaml?

Lets say we have a record which defines students:
type student = {
name : string;
age : int;
grades : (float) list;
}
And safe them in a list like this:
let studentlist = [ {name="alex"; age=7; grades=[1.;2.;3.]} ;
{name="bianca"; age=6; grades=[1.;1.;2.]} ];;
My aim is to compute the grade average of a special student which I choose per age, I select the student with the function search:
let search a lst = List.find( fun {age;_} -> a = age)lst
And compute the average with the help-functions , named sum, length and finally avr :
let rec sum lst =
match lst with
| [] -> 0.0
| h :: t -> h +. sum t
let length lst = float_of_int (List.length lst);;
let avr lst = sum lst /. length lst;;
I don't know how to combine those functions to compute the average properly!
Most of what you've done seems to work. For instance, search works.
utop # search 7 studentlist;;
- : student = {name = "alex"; age = 7; grades = [1.; 2.; 3.]}
If you want to access the grades field of that record, use . for record access.
utop # (search 7 studentlist).grades;;
- : float list = [1.; 2.; 3.]
Now that you have a list of float values, finding the sum or average of them should be easy by passing that value as an argument to the relevant function you've already defined.
Bear in mind that when you use List.find in search, if you search for an age that is not present, you will get a Not_found exception that you will want to handle.
As an aside, note that your avr function iterates over the list twice. Once to compute the sum, and ocne to compute the length.
It is possible to computer the sum, the length, and the average in a single pass. We can use a fold to do this. First off, we can define a basic left fold:
let rec foldl f init lst =
match lst with
| [] -> init
| x::xs -> foldl f (f init x) xs
Consider using this to compute the length of a list:
foldl (fun i _ -> i + 1) 0 [1.; 2.; 3.]
When evaluated:
foldl (fun i _ -> i + 1) 0 [1.; 2.; 3.]
foldl (fun i _ -> i + 1) (0 + 1) [2.; 3.]
foldl (fun i _ -> i + 1) (1 + 1) [3.]
foldl (fun i _ -> i + 1) (2 + 1) []
3
But we can pass a tuple of values to foldl, building up the length, sum, and average as we go.
utop # let (len, sum, avg) = foldl
(fun (len, sum, avg) x ->
let sum = sum +. x in
let len = len + 1 in
let flen = float_of_int len in
(len, sum, sum /. flen))
(0, 0., 0.)
[1.; 2.; 3.];;
val len : int = 3
val sum : float = 6.
val avg : float = 2.

How can you create a list comprising of calculations on each item of another list in Haskell?

I'm trying to make a function working out (and then outputting as a String) the difference between two elements in a list - 1st and 2nd, then 2nd and 3rd, and so on - I think I'm giving it a good go, but I currently keep running into error whack-a-mole, I've put the current error below, but first, obligatory code dump:
type Name = String
type Coordinates = (Int, Int)
type Pop = Int
type TotalPop = [Pop]
type City = (Name, (Coordinates, TotalPop))
testData :: [City]
testData = [("New York City", ((1,1), [5, 4, 3, 2])),
("Washingotn DC", ((3,3), [3, 2, 1, 1])),
("Los Angeles", ((2,2), [7, 7, 7, 5]))]
getPopGrowth :: [City] -> Name -> String
getPopGrowth cs name = concat
[getPercentages z ++ "\n" | (x,z) <- maybeToList (lookup name cs)] where
getPercentages z = unwords (map show z1) ++ "% " where
z1 = percentageIncrease z
percentageIncrease :: [Int] -> [Float]
percentageIncrease (x:xs)
| length (x:xs) > 2 = percentageIncrease (tail xs)
| otherwise = (a / b - 1) * 100.0 where
a = fromIntegral x :: Float
b = fromIntegral (head xs) :: Float
And the error I'm getting at the moment is:
error:
• Couldn't match expected type ‘[Float]’ with actual type ‘Float’
• In the expression: (a / b - 1) * 100.0
In an equation for ‘percentageIncrease’:
percentageIncrease (x : xs)
| length (x : xs) > 2 = percentageIncrease (tail xs)
| otherwise = (a / b - 1) * 100.0
where
a = fromIntegral x :: Float
b = fromIntegral (head xs) :: Float
|
92 | | otherwise = (a / b - 1) * 100.0 where
| ^^^^^^^^^^^^^^^^^^^
I would like to emphasise, I understand the error, but I do not know how to resolve it in such a way that I get the desired outcome of the function.
Just for some clarity around what I'm trying to do.
Input: getPopGrowth testData "New York City"
should Output: 25% 33.333% 50%
So far, you only calculate the percentage when the list has exactly two elements left. Less elements are not covered, and for longer lists, in all the steps before, the elements get dropped without further action. In the last step, however, you return a single Float instead of a list.
The following example creates an increase percentage in every step, concatenating it with the list resulting from applying the function to the tail of the list. Also, the base cases are all covered:
percentageIncrease :: [Int] -> [Float]
percentageIncrease [] = []
percentageIncrease (x:[]) = []
percentageIncrease (x:y:xs) = ((a / b - 1) * 100.0) : percentageIncrease (y:xs) where
a = fromIntegral x :: Float
b = fromIntegral y :: Float
Console output:
*Main> getPopGrowth testData "New York City"
"25.0 33.333336 50.0% \n"

Taking one element from both list if they are not the same/taking specific 2 elements from one list

I have two lists that contain this datatype:
data Student = Student {firstName::String, lastName::String, age::Int}
deriving (Show, Read, Eq)
I need to make another list that will have unique elements from both of this list.
Ideally this list would contain only firstNames as they can only differ in this one field firstName.
I sort of solved, my solution goes like this:
listOfChanges :: [Student] -> [Student] -> [String]
listOfChanges list1 list2 =
let c = intersect list1 list2
b = union list1 list2
in listEvent(nSame b c)
nSame (x:xs) ys = if x `elem` ys
then nSame xs ys
else x:nSame xs (delete x ys)
nSame [] _ = []
nSame _ [] = []
extractName::Student -> String
extractName (Student firstName _ _) = firstName
listEvent :: [Student] -> [String]
listEvent list = map extractName list
But I would like to convert final list to list of type:
data StudentsFirstNameChangeEvent = StudentsFirstNameChangeEvent {oldName::String, newNames::String}
deriving (Show, Read, Eq)
My final list:
["Alicja","Batrek","Damian","AlicjaX","BatrekX","DamianX"]
"Alicja","Batrek","Damian" - oldNames.
"AlicjaX","BatrekX","DamianX" - newNames.
When it actually should be:
[StudentsFirstNameChangeEvent {oldName = "Alicja", newName = "Alicjax"},StudentsFirstNameChangeEvent {oldName = "Bartek", newName = "Bartekx"}, ...]
enter code here
I think im done with haskell, but this is my final progress:
correctSplit :: [String] -> [[String]]
correctSplit list =
let x = length list
x1 = (x `div` 2)
in splitEvery x1 list
splitEvery _ [] = []
splitEvery n list = first : (splitEvery n rest)
where
(first,rest) = splitAt n list
proList :: [Student] -> [Student] -> [String]
proList list1 list2 =
let x = listOfChanges list1 list2
x2 = correctSplit x
x3 = x2 !! 0
x4 = x2 !! 1
in merge x3 x4
merge :: [a] -> [a] -> [a]
merge xs [] = xs
merge [] ys = ys
merge (x:xs) (y:ys) = x : y : merge xs ys
And the final list is now:
["Alicja","AlicjaX","Batrek","BatrekX","Damian","DamianX"]
But I still have no idea how to convert either of those list into the type I need.
Well and it turns out that correct way to process this is much shorter:
listOfChanges :: [Student] -> [Student] -> [StudentsFirstNameChangeEvent]
listOfChanges list1 list2 =
let mask = intersect list1 list2
x1 = list1 \\ mask
x2 = list2 \\ mask
in changeList x1 x2
changeList :: [Student] -> [Student] -> [StudentsFirstNameChangeEvent]
changeList a b = zipWith StudentsFirstNameChangeEvent (map firstName a) (map firstName b)

Creating a new entry in a sorted list in Haskell

I implemented following code to make a new entry in a list of persons (including name and birth date). The List is sorted by the alphabet and should maintain this sorting. If a name is the same, the date defines the sorting. The function dateTurn turns the date around and works perfectly fine.
type Lastname = String
type Firstname = String
dateTurn :: (Int, Int, Int) -> (Int, Int, Int)
dateTurn (a,b,c) = (c,b,a)
new :: ((Lastname,Firstname),(Int,Int,Int)) -> [((Lastname,Firstname),(Int,Int,Int))] -> [((Lastname,Firstname),(Int,Int,Int))]
new x [] = [x]
new x (y:ys)
|(fst x)<(fst y) = x:(y:ys)
|(fst x)>(fst y) = y: (new x ys)
|(fst x)==(fst y) = if (dateTurn (snd x))<(dateTurn (snd y)) then y: (new x ys) else (x:y:ys)
There is no error compiling the script. If I add a person tupel to an empty list it works. However, if I add to an non-empty list. The program doesn't stop working, I have to interrupt it to stop working. So, how can this be solved?
Since your operation is generic you should abstract it
insertSorted :: Ord a => a -> [a] -> [a]
insertSorted x [] = [x]
insertSorted x (y:ys) | x <= y = x: y: ys
| otherwise = y: insertSorted x ys
then, instance your types to Ord
data Person = Person { name :: String
, age :: Int
} deriving (Eq, Show)
instance Ord Person where
(Person a _) `compare` (Person b _) = a `compare` b
then
print $ insertSorted (Person "Peter" 5) [ Person "John" 6
, Person "Zorn" 3]