Wondering if I could get some help writing this function. I am trying to create a function that inverts each "pair" in the list.
module Invert where
invert :: [(a,b)] -> [(b,a)]
invert [(a,b)] = [(b,a)]
When I enter invert [(3,1) (4,1) (5,1)]... it is supposed to give me [(1,3) (1,4) (1,5)... But it gives me...
*Invert> [(3,1) (4,1) (5,1)]
<interactive>:2:2:
The function `(3, 1)' is applied to two arguments,
but its type `(t0, t1)' has none
In the expression: (3, 1) (4, 1) (5, 1)
In the expression: [(3, 1) (4, 1) (5, 1)]
In an equation for `it': it = [(3, 1) (4, 1) (5, 1)]
Since lists are recursive data structures, you have to process a list recursively in order to swap all its elements, or use some higher order function that does the processing for you. If you define
invert [(a,b)] = [(b,a)]
it will only convert single-element lists, all other inputs will fail with an error!
Try to think about the input invert gets: It's either an empty list, or a non-empty lists. In the case of a non-empty list, you can swap the first element and convert the rest recursively.
(If you don't want to invert invert yourself, just use
invert = map swap
where swap is from Data.Tuple.)
Best way to solve this: split it into smaller problems, and either find library functions that solve those, or write your own. I always tell beginners that this is a superior exercise than trying to write a function like invert in just one part, because you should be learning the following three things:
How to split a problem into small, reusable pieces.
The standard library functions offered by the language.
How to use recursion to write small, reusable functions like the ones in the standard library.
In this case, we can split the problem into:
Inverting an individual tuple.
Applying a function to all elements of a list, and collecting the list of results.
The second one is just the common map function on lists, which comes with the standard library. You could try writing your own version of it; this sort of thing is always a good exercise for a beginner:
map :: (a -> b) -> [a] -> [b]
map f [] = ...
map f (x:xs) = ...
The first, as Petr points out, is the swap function from Data.Tuple. But we can write our own easily:
swap :: (a, b) -> (b, a)
swap (a, b) = (b, a)
And now, of course:
invert :: [(a, b)] -> [(b, a)]
invert = map swap
So you want to map a function over the list that's of type (a, b) -> (b, a). The function (,) has type b -> a -> (b,a). So if we flip it, we get a -> b -> (b, a). Now if we uncurry that, we get (a, b) -> (b, a):
invert = map (uncurry $ flip (,))
E.g.
> map (uncurry $ flip (,)) [(1, "a"), (2, "b")]
[("a",1),("b",2)]
As an aside, your patten matching doesn't match what you want. The definition
invert [(a,b)] = [(b,a)]
says "match a list with a single tuple in it". If you have a list with multiple tuples, the match will fail. Also, as Josh Lee pointed out, you need commas between tuples in your list.
Related
How can I group this list by second element of tuples:
[(3,2),(17,2),(50,3),(64,3)]
to get something like:
[[(3,2),(17,2)],[(50,3),(64,3)]]
I'm actually a newcomer to Haskell...and seems to be falling in love with it. Hope you would help me find an efficient way.
It sounds like you've already identified that you want Data.List.groupBy. The type of this function is
groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
So it takes a binary predicate, i.e. an equivalence relation determining how to group elements. You want to group elements by equality on the second term of a pair, so you want
groupBy (\x y -> snd x == snd y) myList
Where snd is a built-in function that gets the second element of a pair.
Incidentally, this pattern of "apply a function to two arguments and then apply a binary function to the results" is very common, especially when calling Data.List functions, so Data.Function provides on.
on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
Weird signature, but the use case is just what we want.
((+) `on` f) x y = f x + f y
So your desired groupBy can be written as
groupBy ((==) `on` snd)
Note that groupBy only finds consecutive equal elements. You didn't indicate whether you wanted consecutive equal elements or all equal elements, but if you want the latter, then I don't believe Haskell base provides that function, though you could certainly write it recursively yourself.
I'm trying to take a list of lists of tuple and turn each list of tuples into a single tuple. Like this:
Currently have:
[[("Erikson,Ann",2.0,3),("Erikson,Ann",3.33,3)],[("Lewis,Buck",2.66,1),
("Lewis,Buck",2.0,3)],[("Smith,John",0.0,1),("Smith,John",1.66,3),
("Smith,John",1.33,3)],[("Torvell,Sarah",4.0,3)]]
And I want the form to be a single list of tuples. One tuple for each persons name.
Along with combining the list of tuples into a single tuple I want to use the second and third elements of each tuple to calculate the gpa of the person. The second number is the grade point for that class and the third number is the credits for the class.
What I need to do is take the sum of credits * gradepoint for each tuple and then divide that sum by the sum of all the credits in each tuple.
What i have so far, that doesn't work is this...
calcGPA :: MyType2 -> MyType2 -> (String, Double, Double)
calcGPA (a,b,c) (d,e,f) = (a, ((b*(fromIntegral c))+(e*(fromIntegral
f))/(b+e)),
(b+e))
Where i am passing in the first list of lists I show at the top of this post.
Am I going in the right direction to solve this problem. Any tips or help would be appreciated.
Thank you
EDIT
Thank you for the help! Helped me understand what was actually going on. I wrote the cumulativeSums fuction as follow:
cumulativeSums :: (Double, Int) -> (String, Double, Int) -> (Double,
Int)
cumulativeSums (a,b) (c,d,e) = (a+(d*e), b+e)
I'm confused on the chunk of code you have above with the let. Where does this go? Do I put it in its own function that I call passing in the list of list of tuples?
Thank you
________________________________________________________________________________Now that im trying to output credits also
calcGPA :: [(String, Double, Int)] -> (String, Double, Int)
calcGPA grades = let name = (\ (name, _, _) ->
name) (head grades)
(name, weightedSum, sumOfWeights) = foldl
cumulativeSums (name, 0, 0) grades
gpa = weightedSum / sumOfWeights
in (name, gpa, credits)
You're going in the right direction if you were planning on using foldl or foldr with your calcGPA function.
What we do when folding is we have a function with the result-so-far, the next element in a list, and the result-just-after. With foldl, which is most appropriate for sums, the type and arguments, as far as lists are concerned is:
foldl :: (b -> a -> b) -> b -> [a] -> b
foldl f startingResult items = …
We see that your function will need to be type (b -> a -> b). Looking elsewhere in the type signature, the ultimate result of foldl is type b. The type of elements in the list is type a.
So what the function you provide foldl does is takes two arguments: the result-so-far and the next item in the list. It then expects your function to give back the result-just-after.
You "fold" in a new item to the result each time your function is run on the next element in the list. So let's look at what our list element type is and what our result type will be.
Our list element type is something like (String, Double, Int). Our result type is (Double, Int). So the type signature for our folding function is:
cumulativeSums :: (Double, Int) -> (String, Double, Int) -> (Double, Int)
So far so good. Now what about the other arguments to foldl? We know the items argument: it's our sublist for one person's grades. We know f, it's our cumulativeSums function we're going to write. What is startingResult? Well, both sums should start with 0, so it's (0, 0). We have:
let name = (\ (name, _, _) -> name) (head grades)
(weightedSum, sumOfWeights) = foldl cumulativeSums (0, 0) grades
gpa = weightedSum / sumOfWeights
in (name, gpa)
Now we write cumulativeSums. Remember, we're getting told the result-so-far and the item from the list. We just need to give back the result-just-after. See if you can write that part.
For the code already provided, I'd recommend writing your own version of it. There are some type errors related to mixing Ints and Doubles in the code above.
You need to go over each sub-list so you can accumulate values. Something like this:
averageGdp :: [[(String, Double, Double)]] -> [(String, Double, Double)]
averageGdp = fmap f
where
f = (,,) <$> fst . head <*> totalAvg <*> totalCredit
fst (a, _, _) = a
totalCredit = getSum . foldMap (\(_, _, c) -> pure c)
total = getSum . foldMap (\(_, b, c) -> pure $ b * c)
totalAvg = (/) <$> total <*> totalCredit
f takes the inner list as its input and produces a triple. You then map f over the outer list.
With this sort of grouping problems, I think it's a bit of a red herring that the data already looks grouped. Can you always be sure of that? What if the data looks like the following?
[[("Erikson,Ann",2.0,3),("Erikson,Ann",3.33,3),("Lewis,Buck",2.66,1)],
[("Lewis,Buck",2.0,3)]]
Or like this?
[[("Erikson,Ann",2.0,3),("Erikson,Ann",3.33,3),("Lewis,Buck",2.66,1)], []]
Notice that in the first example, one entry for "Lewis,Buck" is grouped together with entries for "Erikson,Ann". The second example, on the other hand, contains an empty list.
Most attempts I've seen at solving problems like this does so by utilising unsafe (i.e. non-total) functions like head. This can lead to wrong implementations or run-time crashes.
Haskell is a great language exactly because you can use the type system to keep you honest. If the original input wasn't already grouped, it'd be safer to use ungrouped data. Otherwise, you can flatten the input using concat. I'm here assuming that the example data in the OP is called sample:
*Q52527030> concat sample
[("Erikson,Ann",2.0,3.0),("Erikson,Ann",3.33,3.0),("Lewis,Buck",2.66,1.0),
("Lewis,Buck",2.0,3.0),("Smith,John",0.0,1.0),("Smith,John",1.66,3.0),
("Smith,John",1.33,3.0),("Torvell,Sarah",4.0,3.0)]
This gives you a nice flat list on which you can perform a custom grouping operation:
import qualified Data.Map.Strict as Map
arrangeByFst :: Ord a => [(a, b, c)] -> [(a, [(b, c)])]
arrangeByFst = Map.toList . foldl updateMap Map.empty
where updateMap m (x, y, z) = Map.insertWith (++) x [(y, z)] m
Here I've chosen to take a shortcut and use the built-in Map module, but otherwise, writing a function similar to Map.insertWith on a list of tuples isn't too hard.
This function takes a flat list of triples and groups them into pairs keyed by the first element, but with the other element being a list of data.
If you apply that to the flattened sample input, you get this:
*Q52527030> arrangeByFst $ concat sample
[("Erikson,Ann",[(3.33,3.0),(2.0,3.0)]),("Lewis,Buck",[(2.0,3.0),(2.66,1.0)]),
("Smith,John",[(1.33,3.0),(1.66,3.0),(0.0,1.0)]),("Torvell,Sarah",[(4.0,3.0)])]
This is a more robust approach because it doesn't rely on any particular assumptions about how data is ordered.
Each element in this list is a pair, where the first element is the name, and the second element is a list of grades. You can add a function to calculate the GPA of such a pair:
calculateGPA :: Fractional b => (a, [(b, b)]) -> (a, b)
calculateGPA (n, ts) = (n, sumOfGrades ts / numberOfGrades ts)
where
sumOfGrades grades = sum $ map (\(gp, c) -> gp * c) grades
numberOfGrades grades = fromIntegral (length grades)
This function takes as input a tuple where the second element is a list of tuples ts. It calculates sumOfGrades by mapping each tuple of grade points gp and credits c into the product of the two, and then taking the sum of those numbers. It then divides that number by the length of the list of grades.
You can now map the list produced in the previous step to calculate the GPA of each person:
*Q52527030> map calculateGPA $ arrangeByFst $ concat sample
[("Erikson,Ann",7.995),("Lewis,Buck",4.33),("Smith,John",2.9899999999999998),
("Torvell,Sarah",12.0)]
Apart from using Data.Map.Strict, I've deliberately attempted to strike a balance between keeping things basic, but still safe. A more sophisticated approach could have used fmap instead of map, join instead of concat, more point-free style, and so on. There's always room for improvement.
A one-liner to do what you asked about:
import Control.Category ( (>>>) )
g :: [[(t, Double, Double)]] -> [(t, Double, Double)]
g = filter (not . null) >>>
map (unzip3 >>> \ (a,b,c) -> (head a, sum (zipWith (*) b c) / sum c, sum c))
unzip3 :: [(a, b, c)] -> ([a], [b], [c]) is in the Prelude.
>>> is the left-to-right function composition, (f >>> g) x = g (f x).
filter makes sure all empty groups are removed before further processing.
Imagine I have the following list:
lst :: [(Bool, Maybe Integer)]
lst = [(True, Just 3), (True, Nothing), (False, Just 12)]
Using the lens library, I want to extract the elements of the tuples, but I only want it to succeed when the second element is Just. I want some optic, split that works like this:
> lst ^.. folded.split (_1.to not) (_2._Just)
[(False, 3), (True, 12)]
I can implement split myself like this:
split :: Getting (First a) s a -> Getting (First b) s b -> Fold s (a, b)
split a b = folding (\x -> (,) <$> (x ^? a) <*> (x ^? b))
…which seems to work. However, this seems like I must be reinventing the wheel. Is there something already provided by the lens library that accomplishes this in an equally nice way?
The aside combinator takes a Prism that works over the second component of a tuple and returns a Prism that works over the whole tuple:
ghci> lst ^.. folded.aside _Just
[(True,3),(False,12)]
The resulting prism matches when the component is matched, otherwise it fails.
Combining it with to and bimap, we can reproduce your example:
ghci> lst ^.. folded.aside _Just.to (bimap not id)
[(False,3),(True,12)]
To work over the first component, we can use swapped:
ghci> [(Just 3,False)]^..folded.swapped.aside _Just.swapped
[(3,False)]
I've come to the realization that when I have nested data structures, I've been manually writing code to delve into them. Like this:
--one level
Prelude> map (*2) [1,2,3]
[2,4,6]
--nested two levels
Prelude> let t2 = map $ map (*2)
Prelude> t2 [[1,2,3],[4,5,6]]
[[2,4,6],[8,10,12]]
--nested three levels
Prelude> let t3 = map $ map $ map (*2)
Prelude> t3 [[ [1,2,3],[4,5,6] ],[ [1,2,3],[4,5,6] ]]
[[[2,4,6],[8,10,12]],[[2,4,6],[8,10,12]]]
so it occurs to me that I should be able to automatically construct a function for delving into my nested data structures using a higher order function:
Prelude> let t f n = (iterate map f) !! n
<interactive>:35:22:
Occurs check: cannot construct the infinite type: b0 = [b0]
Expected type: (a0 -> b0) -> a0 -> b0
Actual type: (a0 -> b0) -> [a0] -> [b0]
In the first argument of `iterate', namely `map'
In the first argument of `(!!)', namely `(iterate map f)'
Its strikes me that
I understand its finding a list where it expected...something else
I don't know how to fix this - should I write code to do repeated application even though thats what I thought iterate was for?
This seems similar to the concept of "lifting" - but I don't know how to apply that intuition.
The problem is that these "iterations" have different types. For each iteration, you get an extra level of nesting, so you'd want
t f 0 :: a -> b
t f 1 :: [a] -> [b]
t f 2 :: [[a]] -> [[b]]
But iterate :: (a -> a) -> a -> [a] requires that the iterations all have the same type. In fact, a direct implementation of the above would require some form of dependent types since the return type depends on the value of n.
Unless you have a good reason not to, I suggest keeping it simple and just writing out the required number of map calls. It's possible to use Template Haskell to generate them, but this will likely be more trouble than it's worth.
However, if you have complicated nested data structures, you may want to look into SYB which can automatically take care of the boilerplate of applying such transformations in nested structures.
Here's a quick example:
> import Data.Generics
> let t x = everywhere (mkT (*2)) x
> :t t
t :: Data a => a -> a
> t [2,4,6]
[4,8,12]
> t [[2,4,6],[8,10,12]]
[[4,8,12],[16,20,24]]
> t (Just [(1, 2, 3), (4, 5, 6)])
Just [(2,4,6),(8,10,12)]
Think about the type of (iterate map f) !! n. You want it to be a -> a for n = 0, [a] -> [a] for n = 1, [[a]] -> [[a]] for n = 2 - in general, you want the type of this expression to depend on the value of n. But that is impossible to do in Haskell, since it's not a dependently-typed language.
I want to convert list of tuples: [(2,2,2), (3,3,3), (4,4,4), (5,5,5)] to just list:
[2,2,2,3,3,3,4,4,4,5,5,5]
I try this
map (\(a,b,c,d)->a:b:c:d) listOfTuples
but get an error.
Prelude> map (\(a,b,c)->a:b:c) [(1,2,3), (5,6,7)]
<interactive>:1:37:
No instance for (Num [t])
arising from the literal `7' at <interactive>:1:37
Possible fix: add an instance declaration for (Num [t])
In the expression: 7
In the expression: (5, 6, 7)
In the second argument of `map', namely `[(1, 2, 3), (5, 6, 7)]'
Prelude>
How can I do it with lambda? And why my stuff does not work?
a:b:c:d is invalid because it's a : (b : (c : d)). (:) takes a list on the right-hand side and an element to prepend to it on the left-hand side, but d is another element, not (necessarily) a list. You probably mean a:b:c:d:[], which is the same as [a,b,c,d].
If you make that change, the code will run, and produce [[2,2,2], [3,3,3], [4,4,4], [5,5,5]], since map applies the function to each element of the list, and uses its result as the new element; that is, cannot change the structure of the list, only each element independently. If you want it flattened, you'll need to use concat (which has the type [[a]] -> [a], and flattens a list of lists):
concat (map (\(a,b,c,d) -> [a,b,c,d]) listOfTuples)
However, there is a ready-made composition of concat and map, which is more idiomatic to use:
concatMap (\(a,b,c,d) -> [a,b,c,d]) listOfTuples
This can also be done more succinctly with a list comprehension.
Assuming:
let listOfTuples = [(2,2,2), (3,3,3), (4,4,4), (5,5,5)]
then this list comprehension:
concat [[a,b,c] | (a, b, c) <- listOfTuples]
produces:
[2,2,2,3,3,3,4,4,4,5,5,5]