Haskell - counting with foldr - haskell

Working on a Haskell problem, fairly new to the language. I am trying to count the occurence of tuple values that are present in the list of tuples.
My tuples look like this: [(5, [7,2]), (2,[5,7,1,6])]
So far, using foldr, I have done this:
testFunc = foldr (\x-> const succ) 0
However, this only retrieves the count of the left side of the tuple. I am a little confused, how to solve this?
-- Expected output: 6
-- Current output: 2

The type of foldr is:
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
This means that the function it's talking should take the current value and the accumulator and will return a value of the same type as the accumulator. It then takes an initial value for the accumulator, and finally the Foldable thing to iterate over.
The accumulator is clearly the count, and for an empty list, it'll be zero, so there's our initial accumulator value.
We then just have to add the length of the list to that accumulator each time through. We can pattern match this data out. We'll use _ for the first item in each tuple, because we just don't care about that value.
Prelude> testData = [(5, [7,2]), (2,[5,7,1,6])]
Prelude> foldr (\(_, lst) count -> count + length lst) 0 testData
6
Prelude>

Related

Haskell Functions (map,foldr, foldl)

I am struggling to think of a way to utilize these functions for this beginner level coding class that I am taking to learn functional programming in Haskell. The functions I have to write are shown below, asum is supposed to turn a list of integers [a1,a2,..,an] into the alternating sum a1-a2+a3-a4+.… and I am not sure how to approach it with these functions. The xor function is supposed to that computes the XOR of a list of Booleans. I need some help to understand how to use these functions and it would greatly appreciated. I am also new to Haskell so any explanations would help. Thanks I have to use map foldr foldl.
asum :: (Num a) => [a] -> a
xor :: [Bool] -> Bool
I would say start by running the following, one by one, in GHCi:
:t foldr
:info foldr
:doc foldr
:t foldl
:info foldl
:doc foldl
:t map
:info map
:doc map
Or better, open hoogle.haskell.org and search each of the above mentioned functions and click on the first link.
But I agree that Haskell documentation are difficult to read, especially for beginners. I'm a beginner and I have a lot of difficulty reading and understanding them.
Here's a function that uses map and foldr to show how foldr works:
printFoldr xs = foldr (\x acc -> "(" ++ x ++ " + " ++ acc ++ " )") "0" $ map show xs
Now running watch this:
printFoldr [1..5]
-- outputs the following:
"(1 + (2 + (3 + (4 + (5 + 0 ) ) ) ) )"
This shows us how foldr is evaluated. Before going into how foldr is evaluated, let's look briefly at map.
map show [1..5]
-- outputs the following:
["1","2","3","4","5"]
This means that map takes 2 arguments. A list and a function that is applied to each element of the list. The result is a new list with the function applied to each element. Thus, applying show to each number outputs their string representation.
Back to foldr. foldr takes 3 arguments:
a function of type a -> b -> b
an initial value of type b
a list of type [a]
foldr takes each and every value of the provided list and applies this function to it. What is special is that map retains the output of the function over each iteration and passes it to the function as its second argument on the next run. Therefore it is convenient to write the function that is passed foldr as follows: (\el acc -> do something). Now on the next iteration of foldr, acc will hold the value of the previous run and el will be the current element from the list. BTW, acc stands for accumulator and el for element. This enables us to reduce elements of the provided list to something completely new.
As you can see in printFoldr, the initial value is just an empty string but it gradually adds the lists elements to it showing how it would have reduced the elements of the list to their sum.
Here's an idea:
a1-a2+a3-a4+...
=
a1-(a2-(a3-(a4-(...(an-0)...))))
This fits pretty well to the foldr pattern of recursion,
foldr f z [a1,a2,a3,a4,...,an]
=
a1`f`(a2`f`(a3`f`(a4`f`(...(an`f`z)...))))
So it can be coded by setting f = ... and z = ... and calling
asum :: (Num a) => [a] -> a
asum xs = foldr f z xs
where
f = (...)
z = (...)
You will need to complete this definition.
For the XOR of a list of Booleans, assuming it is to be True if one and only one of them is True, and False otherwise, we can imagine this sequence of transformations:
[ True, False, False, True, True, False, ...]
==>
[ t, f, f, t, t, f, ...]
where t and f are some specially chosen numbers. And then we can find the sum of this second list (not alternating sum, just a sum of a list of numbers) and check whether it is equal to ... some (other?) special number, let's call it n1:
xor :: [Bool] -> Bool
xor bools = (aNumber ... n1)
where
list1 = bools
list2 = fun1 transform list1
transform False = f
transform True = t
f = ...
t = ...
aNumber = sum list2
n1 = ...
fun1 = ...
sum listOfNums = ...
fun1 is the function which transforms each element of its argument list according to the given function, called transform above. It is one of the two functions left from the three you were given, considering we've already been using foldr.
sum is to be implemented by using the last function that's left.
FYI,
map foo [a1,a2,a3,...,an]
=
[foo a1, foo a2, foo a3, ..., foo an]
and
foldl f z [a1,a2,a3,...,an]
=
((((z`f`a1)`f`a2)`f`a3)...)`f`an

Calculating from multiple tuples

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.

Haskel '!!' defined in foldl

I just started learning Haskell and I'm trying to define the "Get n'th element of list" (with the !! operator) in a function which uses foldl. I now defined it without foldl, just making use of recursion. I wondered if anybody could tell me how to change the code I have to a function with foldl, and could describe what is happening. Thanks in advance!
get 1 (x:_) = x
get i (_:xs) = elementAt'' (i - 1) xs
A couple of notes:
First note that you want get 1 to return the first element in your list, that's not the common choice in many languages including Haskell ([2, 3, 5] !! 1 = 3).
Second, although elementAt is a recursive function over lists, it can be defined more efficiently in the old fashion recursive way. fold functions are not good choices here, because fold goes through every element of the list. But we want elementAt recursion to stop the moment that we find the element.
Given this, here is how you can implement elementAt recursively:
elementAt :: Int -> [a] -> a
elementAt i (x:xs) = if i == 1 then x else elementAt (i-1) xs
And here's the implementation using foldl:
elementAt' :: Int -> [a] -> a
elementAt' i (x:xs) =
snd $
foldl
(\(j, a) b ->
if j < 1 then (j-1, a) else (j-1, b))
(i-1, x)
xs
The seed value of foldl is a tuple: (i-1, x) where x is the head of the list.
Note that the return result of fold functions must be of the same type of their seed. Hence here foldl returns a tuple: (j-1, a) where a is the final result, if the index is found; otherwise (j-1, b) where b is the current element of the list.
You can see how foldl goes through every element of the list even after it found the element at index that we were looking for (it keeps returning the previous result a that will be the final result).
PS. These elementAt functions are not handling the case for empty lists or when i is greater than the length of the list; hence they're not exhaustive.
I can see the following, a bit cryptic way of using foldl for your purpose (it is using zero-based indexing, but can be changed easily to 1-based):
get i lst=
snd $
foldl (\p (j, y) -> if j == i then (j,y) else p ) (0, 0) (zip [0,1..] lst)
The foldl part is working with tuples (index, element), whose list is generated by zipping the given list with indices list. The function passed to foldl as first argument is comparing the index of the desired element with the index with currently passed, and returning the current element if the index is matching, or the previous element otherwise. Then, in the end by using snd only the element part of the tuple is returned.

Stuck - Practice exam Q for Haskell coding: Return the longest String in a list of Strings

The full practice exam question is:
Using anonymous functions and mapping functions, define Haskell
functions which return the longest String in a list of Strings, e.g.
for [“qw”, “asd”,”fghj”, “kl”] the function should return “fghj”.
I tried doing this and keep failing and moving onto others, but I would really like to know how to tackle this. I have to use mapping functions and anonymous functions it seems, but I don't know how to write code to make each element check with each to find the highest one.
I know using a mapping function like "foldr" can make you perform repeating operations to each element and return one result, which is what we want to do with this question (check each String in the list of Strings for the longest, then return one string).
But with foldr I don't know how to use it to make checks between elments to see which is "longest"... Any help will be gladly appreciated.
So far I've just been testing if I can even use foldr to test the length of each element but it doesn't even work:
longstr :: [String] -> String
longstr lis = foldr (\n -> length n > 3) 0 lis
I'm quite new to haskell as this is a 3 month course and it's only been 1 month and we have a small exam coming up
I'd say they're looking for a simple solution:
longstr xs = foldr (\x acc -> if length x > length acc then x else acc) "" xs
foldr is like a loop that iterates on every element of the list xs. It receives 2 arguments: x is the element and acc (for accumulator) in this case is the longest string so far.
In the condition if the longest string so far is longer than the element we keep it, otherwise we change it.
Another idea:
Convert to a list of tuples: (length, string)
Take the maximum of that list (which is some pair).
Return the string of the pair returned by (2).
Haskell will compare pairs (a,b) lexicographically, so the pair returned by (2) will come from the string with largest length.
Now you just have to write a maximum function:
maximum :: Ord a => [a] -> a
and this can be written using foldr (or just plain recursion.)
To write the maximum function using recursion, fill in the blanks:
maximum [a] = ??? -- maximum of a single element
maximum (a:as) = ??? -- maximum of a value a and a list as (hint: use recursion)
The base case for maximum begins with a single element list since maximum [] doesn't make sense here.
You can map the list to a list of tuples, consisting of (length, string). Sort by length (largest first) and return the string of the first element.
https://stackoverflow.com/a/9157940/127059 has an answer as well.
Here's an example of building what you want from the bottom up.
maxBy :: Ord b => (a -> b) -> a -> a -> a
maxBy f x y = case compare (f x) (f y) of
LT -> y
_ -> x
maximumBy :: Ord b => (a -> b) -> [a] -> Maybe a
maximumBy _ [] = Nothing
maximumBy f l = Just . fst $ foldr1 (maxBy snd) pairs
where
pairs = map (\e -> (e, f e)) l
testData :: [String]
testData = ["qw", "asd", "fghj", "kl"]
test :: Maybe String
test = maximumBy length testData
main :: IO ()
main = print test

Implementing last with foldr1

I've a problem with implementing the last function using foldr1. I assume that it takes the right most element and treats it like the accumulator value, then applies binary function to the accumulator and its neighbor until it reaches the beginning of a given list. However, the code shown below doesn't work. Could someone tell me what's wrong with it? Instead of returning the last element it prints out the first one
last' list = foldr1 (\acc _ -> acc) list
foldr :: (a -> b -> b) -> b -> [a] -> b
As you can see, foldr makes the accumulator the second argument, while the value from the list is the first. foldr1 behaves the same way. So, as Lee said, you need to have your lamdba return the second argument rather than the first. Alternatively, you could say foldr1 (flip const)

Resources