Haskell Adding Elements to Tuples/Lists in order - list

I have defined a list of (String, Int) pairs.
type PatientList = [(String,Int)]
I need to add data to this list in form 'name' and 'number', where number will increment for each addition to the list, for example the list (or tuple) after 3 name additions will look like:
[("bob", 1), ("ted", 2), ("harry", 3)]
Name will be captured using the following code:
do putStr "You are? "
name <- getLine
My current solution is to create a list of names e.g. (bob, ted, harry) and then using zip, combine these lists as follows:
zip = [1...]["bob","ted","harry"]
This solution doesn't fulfil my requirements as I wish to add to the list at different times and not combine together. How can I do this?

Isn't it better to keep the list in reverse order?
[("harry", 3), ("ted", 2), ("bob", 1)]
Than adding will be in constant time:
add :: PatientList -> String -> PatientList
add [] newName = [newName]
add ((oldName, x):xs) newName = (newName, x+1):(oldName, x):xs
When you need whole list in order, you just in O(lenght yourList) linear time:
reverse patientList

You could use an IntMap, from the containers package.
import Data.IntMap (IntMap)
import qualified Data.IntMap as IntMap
type PatientList = IntMap String
registerPatient :: PatientList -> String -> PatientList
registerPatient pList name
| IntMap.null plist = IntMap.singleton 1 name
| otherwise = let (n, _) = findMax pList
in IntMap.insert (succ n) name plist

As said before if speed isn't a concern use length
add :: String -> [(String, Int)] -> [(String, Int)]
add name xs = xs ++ [(name, length xs)]
But if you remove an element this will mess up you id so maybe
add name xs = xs ++ [(name, 1 + ( snd (last xs) ) )]
I haven't tried running any of this because I'm not a computer with ghc, but you should get the idea.

Related

Accessing specific elements in a list of tuples in f# without libraries

I have a list of tuples with three different elements like so:
[(a0:string, b0:string, c0:int); (a1, b1, c1); (and so on...)].
I need to make a function that takes this list and a "name" in the form of a and gives a list of all the bs where the name matches the a in the tuple. But I'm not sure how to iterate and match everything.
input: function name tuplelist
output: [b0 if a0 = name; b1 if a1 = name; b2 if a2 = name]
I also cannot use libraries!!!
When writing code in a functional style you will often use recursion, if not explicitly then implicitly since most list/array/sequence functions all use recursion under the hood.
In F#, you need to explicitly state that a function is recursive, so the function you create will use the let rec syntax in its definition. Given your requirements, your function will probably look like this:
let rec myFilter (name: string) (input: (string * string * int) list) =
...
For this class of problems, where you iterate over a list recursively, you usually use pattern matching to check if you are at the end of a list, and if so, return an empty list.
let rec myFilter (name: string) (input: (string * string * int) list) =
match input with
| [] -> []
...
Now you need to write a pattern match that checks the first item in the tuple against the supplied name. You can use pattern matching on the head of the list, and F#'s when syntax to deconstruct the head of the list for comparison
let rec myFilter (name: string) (input: (string * string * int) list) =
match input with
| [] -> []
| ((a, b, _) :: rest) when a = name -> b :: myFilter name rest
...
This second case matches when a matches the queried name. When it matches, it will return a new list of which b is the head of the list, and then it will take the rest of the list of tuples and call myFilter recursively. This is how you iterate through the list recursively.
We have one more case to check for: If we don't find a match, we want to keep stepping through the list without collecting b. This can be expressed by peeling off the head and recursively calling myFilter, sending only the rest of the tuples.
let rec myFilter (name: string) (input: (string * string * int) list) =
match input with
| [] -> []
| ((a, b, _) :: rest) when a = name -> b :: myFilter name rest
| (_ :: rest) -> myFilter name rest
Calling myFilter "a" [("a","x",3);("b","y",4);("a","z",5)] then yields ["x"; "z"], as expected.
Powerful F# pattern matching and recursion along with type inference easily compensate for dropping libraries limitation.
You need to build a map function converting one list into another list, this is to be addressed with recursion, the mapping function applied to each element of the list may use pattern matching to disassemble tuple into components and perform the conversion.
Something like the following will do:
let filter name (tupleList:(string*string*int) list) =
let checkElement name = function | (a,b,c) -> if a = name then Some b else None
let rec mapList name inputList outputList =
match inputList with
| [] -> outputList
| h::t -> let filter = checkElement name h
mapList name t (if filter = None then outputList else (outputList # [filter.Value]))
mapList name tupleList []
Here checkElement is the mapping function that takes the name and a tuple (a,b,c) and returns an option value either Some b if a = name, or None if not.
Recursive function mapList on each step operates with the unprocessed part of inputList of tuples and outputList accumulating on each recursion step parts from matched elements only. On each recursion step it checks if the inputList is empty. If yes, then we are done and it's time to return the accumulated result, otherwise we split the head element off inputList and apply the mapping function to it, changing the accumulated list, if this is the case. Then we do the next recursion step on the tail of inputList and the accumulator.
This task requires you to write your own recursive list processing function. I was able to implement it by using two sub-functions.
While the answers by #GeneBelitski and #ChadGilbert are great for learning, they are not tail-recursive, so I'll add my own.
Sub-function aux takes an accumulator and the list to process, and matches against the shape of the list. If it is empty, it returns the result accumulated so far. If it is a 3-tuple with a tail, it compares the first element to name, and if they are equal, continues running itself prepending the second element to the results accumulated, and the tail of the original list, otherwise just the results accumulated so far, and the tail of the original list.
As this way of accumulating results inverts the order of the resulting list, we need to reverse it before returning it; this is what the sub-function rev does, and as you see, the code looks almost identical to aux, prepending elements to the results accumulated so far but without doing any comparing or processing.
let filter_b name lst =
let rec aux acc = function
| [] -> acc
| (a : string, b : string, c : int) :: tl ->
if a = name
then aux (b :: acc) tl
else aux acc tl
let rec rev acc = function
| [] -> acc
| hd :: tl -> rev (hd :: acc) tl
lst
|> aux [] // process list with aux function
|> rev [] // reverse resulting list

Haskell: How do I combine a list of list of tuples using foldr?

I want to combine my list but I need to pass in a dummy initial value which is why I want to use 'foldr'.
My current list is:
[[(1, "Bob J", 4.0)],[(1, "Bill J", 2.5),(2,"Bill J", 2.7)]]
I want to combine the list based on their names but I also need to do arithmetic when I am combining the number.
My current code is:
grp xs = foldr combine xs
where
combine
(x,_,y)
(totalX,_,totalY)
= (totalX+x,_,totalY+fromIntegral x *y)
This returns an error. I want totalX and totalY to initially be zero so it doesn't mess with the calculations. How do I do this?
Edit: I want my output to be in the same format so I want:
[(1,"Bob J", 4.0],(3,"Bill J", 2.6)]
First I would flatten your list (use concat), then accumulate the values into a Map recursively. While accumulating, I would ensure that you upsert existing values with addition (this is what the add function does).
module Main where
import Data.List
import qualified Data.Map as M
main = print $ makeMap vals
makeMap :: (Ord k, Num a, Num b) => [(a, k, b)] -> M.Map k (a, b)
makeMap = foldl' go mempty
where
go m (x,name,y) = M.insertWith add name (x,y) m
add (a,b) (c,d) = (a+c,b+d)
vals = concat [[(1, "Bob J", 4.0)],[(1, "Bill J", 2.5),(2,"Bill J", 2.7)]]
Result is
λ> main
fromList [("Bill J",(3,5.2)),("Bob J",(1,4.0))]

Add a list to a list of lists in Haskell

How can I add a list to a list of lists? Say I want to add itemz to bagList, which is a list of lists. How can I do that?
bagList itemz = mappend bagList itemz
You might want to consider adding it at the front, this is faster:
bagItem bag item = item : bag
Also it looks like you're coming from an imperative mindset, the way you use bagList before and after the = is not quite right: the expressions before and after the = do not really represent the same construction. Before the = bagItem is used as a function, after the = it's used as some Monoid (which if itemz is a list would also need to be a list).
If you really do want to append the item (this will be slower, because the operation will require going all the way through the list to add the new item at the end, and the whole list will need to be reconstructed) you can do what Christoph suggests or you can go for a recursive formulation something like this:
appendItem :: a -> [a] -> [a]
appendItem i (x:xs) = x : appendItem i xs
appendItem i [] = i : []
If you both want to append and are also worried about performance, you should have a look at difference lists, for example look for the section on difference lists in this chapter in Learn You a Haskell.
Update
From the comments it seems what you are actually looking for is Maps. We can make a Map with each item as a key, and the number of occurrences as the value. In your case it seems this will be a Map String Int.
import Data.List (foldl')
import qualified Data.Map as M
bag :: M.Map String Int
bag = M.empty
addToBag :: M.Map String Int -> [String] -> M.Map String Int
addToBag = foldl' go
where go m i = M.insertWith (+) i 1 m
main = print $ addToBag bag ["a","b","c","a"]
-- fromList [("a",2), ("b", 1), ("c", 1)]

Functionality of adding to lists in Haskell / overwriting an existing List

type Dictionary = [(String, String)]
dict :: Dictionary
dict = ("Deutsch", "English"):[]
insert :: Dictionary -> (String,String) -> Dictionary
insert dict entry = dict ++ [entry]
One thing that I didn't find about the way lists work: Is it somehow possible to overwrite the existing dict with the entry added in insert? Or is it necessary to, in the next step, always write out the list that was put out by insert?
insert [("German", "English"), ("Hallo", "hello")] ("Versuch", "try")
So far, this is the only way I have been able to add something to the new list without losing the previous entry. However, next on the list of things to implement is a search command, so I wonder if I'd also have to write this out in the search function.
The idea of functional programming is in general that your data is immutable. This means once you have created a list, you can NEVER change that list. But you can copy that list, make modifications to it, and keep that as well.
So when you have a list like so
test = [1,2,3]
We can modify this by adding 4 to the start:
test2 = 4 : test
: called the cons operator, puts an element in front of a list. Do note that x:xs (the same as doing [x]++xs) has a better performance than doing xs++[x]
So now we have two bindings, one of test to [1,2,3] and one of test2 to [4,1,2,3]
Hope this clarifies things
To give a full example:
type Dictionary = [(String, String)]
insert :: Dictionary -> (String,String) -> Dictionary
insert dict entry = dict ++ [entry]
dict0 = [ ("Deutsch", "English") ]
dict1 = insert dict0 ("Hallo", "hello")
dict2 = insert dict1 ("Versuch", "try")
If you're new to functional programming, I would recommend reading Learn You a Haskell for Great Good , which is a fantastic (and free) book on how to use Haskell -- and functional programming in general.
It's not too tough to do this
import Data.List (lookup)
insert :: Eq a => (a,b) -> [(a,b)] -> [(a,b)]
insert (a,b) [] = [(a,b)]
insert (a,b) ((c,d):rest) = if a == c
then (a,b) : rest
else (c,d) : insert (a,b) rest
---
dict :: [(String, String)]
dict = [("Deutsch", "English")]
If you can't use Data.List then you can define lookup by
lookup :: Eq a => a -> [(a,b)] -> Maybe b
lookup _ [] = Nothing
lookup k ((a,b):rest) = if k == a then Just b else lookup k rest
Now if you load up GHCI:
>> let dict' = insert ("Ein","One") dict
>> dict'
[("Deutsch","English"),("Ein","One")]
>> lookup "Ein" dict'
Just "One"
>> insert ("Deutsch", "Francais") dict'
[("Deutsch","Francais"),("Ein","One")]
If you want to replace an existing pair with the same key then you could write insert as:
insert :: Dictionary -> (String, String) -> Dictionary
insert [] p = [p]
insert ((dk, dv):ps) p#(k, v) | dk == k = p:ps
insert (p:ps) ip = p : (insert ps ip)
However if you are writing an association list, then you can simplify it by inserting new items at the front of the list:
insert :: Dictionary -> (String, String) -> Dictionary
insert = flip (:)
if you then search from the front of the list, it will find any values added more recently first.
In Haskell, most values are immutable, meaning that you can not change their value. This seems like a huge constraint at first, but in reality it makes it easier to reason about your program, especially when using multiple threads.
What you can do instead is continually call insert on the dictionary returned when you call insert, for example:
mainLoop :: Dictionary -> IO ()
mainLoop dict = do
putStrLn "Enter the German word:"
german <- getLine
putStrLn "Enter the English word:
english <- getLine
let newDict = insert dict (german, english)
putStrLn "Continue? (y/n)"
yesno <- getChar
if yesno == 'y'
then mainLoop newDict
else print newDict
main = do
One simply can't 'overwrite' anything in a pure language (outside of ST monad). If I understood your question correctly, you are looking for something like this:
insert :: Dictionary -> (String,String) -> Dictionary
insert [] b = [b] -- If this point is reached where wasn't matching key in dictionary, so we just insert a new pair
insert (h#(k, v) : t) b#(k', v')
| k == k' = (k, v') : t -- We found a matching pair, so we 'update' its value
| otherwise = h : insert t b

Haskell select elements in list

I've a problem with a function like this:
data City = City {Car :: String, Weight :: Int, Color :: String}
-I've a list of "City" and my function has to make a list of tuples, and each tuple is (Car, "sum of the weight"), therefore, if a the Car is equal, the weight has to be added, making something like this:
main> [(Porche,180),(Ferrari,400),(Opel,340)]
The car's can't be repeated on the output list, because their wheights must be added.
I was thinking of doing something like making a list with all the car types, and then filtering the weights and add them, making a list, but I just can't make it work.
I will guide you to the solution. It is better to understand how to arrive at the solution than the solution itself.
import Data.List
data City = City {car :: String, weight :: Int, color :: String} deriving (Show)
If color has nothing to do with City being equal you can convert the City to a tuple. You can use map to do that.
city2tup :: [City] -> [(String,Int)]
city2tup = map (\(City c w _) -> (c,w))
Now look at function sort and groupBy from Data.List. Sorting and then grouping on the first element will collect together similar cars in a list. So you will have a list of list.
Now you just need to fold on each sublist and add corresponding weights.
collect :: [City] -> [(String,Int)]
collect = map (foldl1 collectWeight) . groupBy ((==) `on` fst) . sort . city2tup
You still need to define what collectWeight is but that should be easy.
In terms of performance, perhaps it's best to use the Data.HashMap.Lazy package for this job. Accordingly you may do it as follows;
import qualified Data.HashMap.Lazy as M
data City = City {car :: String, weight :: Int, color :: String}
ccw :: [City] -> [(String, Int)]
ccw [] = []
ccw (x:xs) = M.toList $ foldr addWeight (M.singleton (car x) (weight x)) xs
where
addWeight :: City -> M.HashMap String Int -> M.HashMap String Int
addWeight c r = case M.lookup (car c) r of
Nothing -> M.insert (car c) (weight c) r
Just x -> M.adjust (+ weight c) (car c) r
λ> ccw [City "Porsche" 180 "Black", City "Ferrari" 400 "Red", City "Opel" 340 "White", City "Porsche" 210 "Yellow"]
[("Opel",340),("Ferrari",400),("Porsche",390)]