Recursion over Lists - Haskell - list

Basically I have this exercise: Recall the StudentMark type synonym from last week. Write a recursive function:
listMarks :: String -> [StudentMark] -> [Int]
which gives a list of the marks for a particular student; for example:
listMarks "Joe" [("Joe", 45), ("Sam", 70), ("Joe", 52)] = [45,52]
This was the way I wrote the function:
type StudentMark = (String, Int)
listMarks :: String -> [StudentMark] -> [Int]
listMarks _ [] = []
listMarks std (x:xs)
| std == fst x = snd x : listMarks (fst x) xs
| otherwise = listMarks (fst x) xs
This does not work if a string from the list is different from the "std" string. I would like to understand why and how could I make this work? Thank you!

Easy Fix
Just change the guard | otherwise = listMarks std xs. I would also change it in the guard above, as | std == fst x = snd x : listMarks std xs as yes, they are equal, but it makes it more clear what you want to achieve. so your code would be:
type StudentMark = (String, Int)
listMarks :: String -> [StudentMark] -> [Int]
listMarks _ [] = []
listMarks std (x:xs)
| std == fst x = snd x : listMarks std xs
| otherwise = listMarks std xs
Better Versions
As you can see, you ae calling the function with always the same first argument, so it's highly likely you can write a neater version. Here are two quick ideas:
List Comprehension
Personally my favourite, list comprehensions are very versitile and clear:
listMarks' :: String -> [StudentMark] -> [Int]
listMarks' str marks = [m |(n,m) <- marks, n==str]
Basically you filter the list based on the first element, and then you return the second one.
Higher Order Functions
With higher order functions map, filter and fold, you can do as much as recursion and lcs, but often looks tidier. You want to, again, filter the list based on the first element, and then you return the second one.
listMarks'' :: String -> [StudentMark] -> [Int]
listMarks'' str = map snd . filter (\(n,_) -> n == str)

Related

Breaking up a list into sublists with recursion

I'm trying to write a function with the type declaration [(Int, Bool)] -> [[Int]]. I want the function to only add Ints to the same nested sublist if the Boolean is True. However if the Boolean is False, I want the Int associated with the next True bool to be added to a new sublist. For example: An input of
[(1,True),(2,True),(3,False),(4,True),(5,False),(6,False),(7,True)]
should return
[[1,2],[4],[7]].
My code so far:
test:: [(Int, Bool)] -> [[Int]]
test xs = case xs of
[]->[]
x:xs
| snd x == True -> [(fst x)] : test xs
| snd x == False -> test xs
I'm currently having issues on adding concurrent Ints to the same list if their bools are both True.
You can break this problem into two sub-problems.
For any given list, take the head of this list and match it against the rest of list. There are two possibilities during this matching: i) You are successful i.e. you match, and if so, you collect the matched value and continue looking for more values, or ii) You fail, i.e. you don't match, and if so, you stop immediately and return the so far matched result with rest of, not-inspected, list.
collectF :: (Eq a) => (a -> Bool) -> [a] -> ([a], [a])
collectF f [] = ([], [])
collectF f (x : xs)
| f x = let (ys, zs) = collectF f xs in (x : ys, zs)
| otherwise = ([], x : xs)
Now that you have the collectF function, you can use it recursively on input list. In each call, you would get a successful list with rest of, not-inspected, list. Apply collectF again on rest of list until it is exhausted.
groupBy :: (Eq a) => (a -> a -> Bool) -> [a] -> [[a]]
groupBy _ [] = []
groupBy f (x : xs) =
let (ys, zs) = collectF (f x) xs in
(x : ys) : groupBy f zs
*Main> groupBy (\x y -> snd x == snd y) [(1,True),(2,True),(3,False),(4,True),(5,False),(6,False),(7,True)]
[[(1,True),(2,True)],[(3,False)],[(4,True)],[(5,False),(6,False)],[(7,True)]]
I am leaving it to you to remove the True and False values from List. Also, have a look at List library of Haskell [1]. Hope, I am clear enough, but let me know if you have any other question.
[1] http://hackage.haskell.org/package/base-4.12.0.0/docs/src/Data.OldList.html#groupBy
Repeatedly, drop the Falses, grab the Trues. With view patterns:
{-# LANGUAGE ViewPatterns #-}
test :: [(a, Bool)] -> [[a]]
test (span snd . dropWhile (not . snd) -> (a,b))
| null a = []
| otherwise = map fst a : test b
Works with infinite lists as well, inasmuch as possible.
Here's how I'd write this:
import Data.List.NonEmpty (NonEmpty(..), (<|))
import qualified Data.List.NonEmpty as NE
test :: [(Int, Bool)] -> [[Int]]
test = NE.filter (not . null) . foldr go ([]:|[])
where
go :: (Int, Bool) -> NonEmpty [Int] -> NonEmpty [Int]
go (n, True) ~(h:|t) = (n:h):|t
go (n, False) l = []<|l
Or with Will Ness's suggestion:
import Data.List.NonEmpty (NonEmpty(..))
test :: [(Int, Bool)] -> [[Int]]
test = removeHeadIfEmpty . foldr prependOrStartNewList ([]:|[])
where
prependOrStartNewList :: (Int, Bool) -> NonEmpty [Int] -> NonEmpty [Int]
prependOrStartNewList (n, True) ~(h:|t) = (n:h):|t
prependOrStartNewList (n, False) l = []:|removeHeadIfEmpty l
removeHeadIfEmpty :: NonEmpty [Int] -> [[Int]]
removeHeadIfEmpty (h:|t) = if null h then t else h:t

Largest prefix list of lists

I need to define a function largestPrefix that goes through a list of strings
and returns the longest prefix of two adjacent strings of the list in a tuple. The first element of the tuple is the length of the prefix .
a.e.: largestPrefix ["a","abca","bca","bcadabca","ca","cdabca"] => (3,"bca")
I have already built a function for two Stings, but have no idea how to use that on a list of lists. To ensure the adjacency I thought about using this:
prefix:: String -> String -> String
prefix (x:xs) (y:ys) | x:xs == "" = ""
| y:ys == "" = ""
| x == y = x:prefix xs ys
| otherwise = ""
So I cleaned up your function a bit and added the following idea:
if you write a function that applies an operator to every two consecutive pairs of a list (pairwise) then you should be able to finish your work:
pairwise :: (a -> a -> b) -> [a] -> [b]
pairwise f (x:xs#(y:_)) = f x y : pairwise f xs
pairwise _ _ = []
prefix:: String -> String -> String
prefix (x:xs) (y:ys)
| x == y = x:prefix xs ys
| otherwise = ""
prefix _ _ = ""
here is an example:
λ> pairwise prefix ["a","abca","bca","bcadabca","ca","cdabca"]
["a","","bca","","c"]
so maybe someone was unhappy because I did not resolve everything so here you go:
largestPrefix :: [String] -> (Int, String)
largestPrefix xs = maximumBy (compare `on` length . snd) $ zip [1..] (pairwise prefix xs)
where you have to import this to work:
import Data.List (maximumBy)
import Data.Function (on)
example
λ> largestPrefix ["a","abca","bca","bcadabca","ca","cdabca"]
(3,"bca")

How to recursively call a function over the length of the list?

I had an interview question, and it has been bugging me since then.
I have a function, fill, that does the computation like taking two lists and then replacing 2s in the second list, where ever there are 2s in the first list and also once 2s are filled in the second list from the first list, then it can flow till a 1 is encountered. For eg:
Two lists [2,1,2,1,2] [0,0,1,0,0] is passed, so the output I get is [2,2,1,2,2]. Now, I want to write a function that takes an argument something like this: [[2,1,2,1,2],[0,0,1,0,0],[0,0,0,0,0]], I want to apply my above function recursively till the end of this list of lists. So like first [2,1,2,1,2] [0,0,1,0,0] are passed to fill, then it should get the result [2,2,1,2,2], then [2,2,1,2,2] and [0,0,0,0,0] should be passed, getting the result [2,2,2,2,2]. How can I do that?
EDIT:
I did this:
fillAll::[[Int]]->[Int]
fillAll [] = []
fillAll (x:xs) =
(foldl' seep x xs) $
helper2 x
helper2:: [Int] -> Bool
helper2 lst =
if 2 `elem` lst then True else False
So, you have your function fill:
fill :: [Int] -> [Int] -> [Int]
And you want to turn this into a function which takes a list of lists:
fillRec :: [[Int]] -> [Int]
This is a natural case for a fold. This repeatedly 'folds' each element of a list together using a combining function. We need to make sure the list isn't empty:
fillRec [] = []
fillRec (x : xs) = foldl fill x xs
This version of foldl (e.g. folds from the left, rather than from the right) is non-strict, which can cause large memory accumulation. It's better to use the strict variant foldl' from Data.List:
fillRec (x : xs) = foldl' fill x xs
I'm going to assume that you already have fill :: [Int] -> [Int] -> [Int] defined. If so, this problem is pretty easy to solve using a fold. Explicitly, you could do something like
fillAll :: [[Int]] -> [Int]
fillAll [] = []
fillAll (x:xs) = go x xs
where
go first [] = first
go first (second:rest) = go (fill first second) rest
Or you can use one of the built-in folds:
fillAll [] = []
fillAll (x:xs) = foldl fill x xs
but as Impredicative points out, you'll have better performance with foldl' from Data.List

Replace an element in a list only once - Haskell

I want to replace an element in a list with a new value only at first time occurrence.
I wrote the code below but using it, all the matched elements will change.
replaceX :: [Int] -> Int -> Int -> [Int]
replaceX items old new = map check items where
check item | item == old = new
| otherwise = item
How can I modify the code so that the changing only happen at first matched item?
Thanks for helping!
The point is that map and f (check in your example) only communicate regarding how to transform individual elements. They don't communicate about how far down the list to transform elements: map always carries on all the way to the end.
map :: (a -> b) -> [a] -> [b]
map _ [] = []
map f (x:xs) = f x : map f xs
Let's write a new version of map --- I'll call it mapOnce because I can't think of a better name.
mapOnce :: (a -> Maybe a) -> [a] -> [a]
There are two things to note about this type signature:
Because we may stop applying f part-way down the list, the input list and the output list must have the same type. (With map, because the entire list will always be mapped, the type can change.)
The type of f hasn't changed to a -> a, but to a -> Maybe a.
Nothing will mean "leave this element unchanged, continue down the list"
Just y will mean "change this element, and leave the remaining elements unaltered"
So:
mapOnce _ [] = []
mapOnce f (x:xs) = case f x of
Nothing -> x : mapOnce f xs
Just y -> y : xs
Your example is now:
replaceX :: [Int] -> Int -> Int -> [Int]
replaceX items old new = mapOnce check items where
check item | item == old = Just new
| otherwise = Nothing
You can easily write this as a recursive iteration like so:
rep :: Eq a => [a] -> a -> a -> [a]
rep items old new = rep' items
where rep' (x:xs) | x == old = new : xs
| otherwise = x : rep' xs
rep' [] = []
A direct implementation would be
rep :: Eq a => a -> a -> [a] -> [a]
rep _ _ [] = []
rep a b (x:xs) = if x == a then b:xs else x:rep a b xs
I like list as last argument to do something like
myRep = rep 3 5 . rep 7 8 . rep 9 1
An alternative using the Lens library.
>import Control.Lens
>import Control.Applicative
>_find :: (a -> Bool) -> Simple Traversal [a] a
>_find _ _ [] = pure []
>_find pred f (a:as) = if pred a
> then (: as) <$> f a
> else (a:) <$> (_find pred f as)
This function takes a (a -> Bool) which is a function that should return True on an type 'a' that you wan to modify.
If the first number greater then 5 needs to be doubled then we could write:
>over (_find (>5)) (*2) [4, 5, 3, 2, 20, 0, 8]
[4,5,3,2,40,0,8]
The great thing about lens is that you can combine them together by composing them (.). So if we want to zero the first number <100 in the 2th sub list we could:
>over ((element 1).(_find (<100))) (const 0) [[1,2,99],[101,456,50,80,4],[1,2,3,4]]
[[1,2,99],[101,456,0,80,4],[1,2,3,4]]
To be blunt, I don't like most of the answers so far. dave4420 presents some nice insights on map that I second, but I also don't like his solution.
Why don't I like those answers? Because you should be learning to solve problems like these by breaking them down into smaller problems that can be solved by simpler functions, preferably library functions. In this case, the library is Data.List, and the function is break:
break, applied to a predicate p and a list xs, returns a tuple where first element is longest prefix (possibly empty) of xs of elements that do not satisfy p and second element is the remainder of the list.
Armed with that, we can attack the problem like this:
Split the list into two pieces: all the elements before the first occurence of old, and the rest.
The "rest" list will either be empty, or its first element will be the first occurrence of old. Both of these cases are easy to handle.
So we have this solution:
import Data.List (break)
replaceX :: Eq a => a -> a -> [a] -> [a]
replaceX old new xs = beforeOld ++ replaceFirst oldAndRest
where (beforeOld, oldAndRest) = break (==old) xs
replaceFirst [] = []
replaceFirst (_:rest) = new:rest
Example:
*Main> replaceX 5 7 ([1..7] ++ [1..7])
[1,2,3,4,7,6,7,1,2,3,4,5,6,7]
So my advice to you:
Learn how to import libraries.
Study library documentation and learn standard functions. Data.List is a great place to start.
Try to use those library functions as much as you can.
As a self study exercise, you can pick some of the standard functions from Data.List and write your own versions of them.
When you run into a problem that can't be solved with a combination of library functions, try to invent your own generic function that would be useful.
EDIT: I just realized that break is actually a Prelude function, and doesn't need to be imported. Still, Data.List is one of the best libraries to study.
Maybe not the fastest solution, but easy to understand:
rep xs x y =
let (left, (_ : right)) = break (== x) xs
in left ++ [y] ++ right
[Edit]
As Dave commented, this will fail if x is not in the list. A safe version would be:
rep xs x y =
let (left, right) = break (== x) xs
in left ++ [y] ++ drop 1 right
[Edit]
Arrgh!!!
rep xs x y = left ++ r right where
(left, right) = break (== x) xs
r (_:rs) = y:rs
r [] = []
replaceValue :: Int -> Int -> [Int] -> [Int]
replaceValue a b (x:xs)
|(a == x) = [b] ++ xs
|otherwise = [x] ++ replaceValue a b xs
Here's an imperative way to do it, using State Monad:
import Control.Monad.State
replaceOnce :: Eq a => a -> a -> [a] -> [a]
replaceOnce old new items = flip evalState False $ do
forM items $ \item -> do
replacedBefore <- get
if item == old && not replacedBefore
then do
put True
return new
else
return old

Learning Haskell: How to remove an item from a List in Haskell

Trying to learn Haskell. I am trying to write a simple function to remove a number from a list without using built-in function (delete...I think). For the sake of simplicity, let's assume that the input parameter is an Integer and the list is an Integer list. Here is the code I have, Please tell me what's wrong with the following code
areTheySame :: Int -> Int-> [Int]
areTheySame x y | x == y = []
| otherwise = [y]
removeItem :: Int -> [Int] -> [Int]
removeItem x (y:ys) = areTheySame x y : removeItem x ys
The others are right that the problem is the : operator. I would say that your areTheySame function that returns a list is the wrong approach anyway, though. Rather than switch to the ++ operator, a better implementation of that function would be:
removeItem _ [] = []
removeItem x (y:ys) | x == y = removeItem x ys
| otherwise = y : removeItem x ys
As you can see, this is a pretty simple implementation. Also, consing like this is much less taxing for your program than appending a bunch of lists together. It has other benefits as well, such as working lazily.
The : operator doesn't do what you think it does:
(:) :: a -> [a] -> [a]
It takes an item of type a and adds it to the beginning of a list of type a. You're using it to join two lists of type a. For that, you need to use ++:
(++) :: [a] -> [a] -> [a]
Also, if you make a recursive function, it needs an ending condition. So try this:
removeItem _ [] = []
removeItem x (y:ys) = areTheySame x y ++ removeItem x ys
That way, when you get to the end of the list, the function will stop recursing.
You can also do this as a list-comprehension
delete :: Eq a => a -> [a] -> [a]
delete deleted xs = [ x | x <- xs, x /= deleted ]
I wrote a function in just one line of code:
remove element list = filter (\e -> e/=element) list
For example:
remove 5 [1..10]
[1,2,3,4,6,7,8,9,10]
remove 'b' ['a'..'f']
"acdef"
This is the minimal fix to make your example work:
removeItem :: Int -> [Int] -> [Int]
removeItem _ [] = []
removeItem x (y:ys) = areTheySame x y ++ removeItem x ys
First, you need to use ++ to concatenate lists, as the : operator used by you adds just one element to the beginning of a list (it can neither be used to add lists with one element nor to add empty lists). You first compare the head of the list (y) to the item you want to remove and correctly return the item or an empty list using areTheySame. Then you want to recursively continue using removeItem on the rest of the list (ys). The resulting list needs to be concatenated using ++.
Second, as Chris Lutz noted, you need an ending condition when you reach the end of the list. By adding this line, Haskell knows what to do with an empty list (that is, nothing, just return an empty list).
As Chuck said, you can simplify the code for this task by having removeItem not delegate the task of the comparison, but compare itself and throw away the element if it should be removed, otherwise keep it at the list head (using :). In any case, continue recursively with the rest of the list.
-- nothing can be removed from an empty list
-- ==> return empty list and stop recursion
removeItem _ [] = []
-- if the list is not empty, cut off the head in y and keep the rest in ys
-- if x==y, remove y and continue
removeItem x (y:ys) | x == y = removeItem x ys
-- otherwise, add y back and continue
| otherwise = y : removeItem x ys
For reference, you may be interested in seeing how it's done in delete from Data.List.
You could leave areTheySame as is, but you'd then need to use concatMap in removeItem to collapse the empty lists:
removeItem :: Int -> [Int] -> [Int]
removeItem x xs = concatMap (areTheySame x) xs
or equivalently
removeItem :: Int -> [Int] -> [Int]
removeItem x = concatMap (areTheySame x)
Note that the types of your functions could be more general:
areTheySame :: (Eq a) => a -> a -> [a]
removeItem :: (Eq a) => a -> [a] -> [a]
This allows removal of items from lists of any type for which == is defined, not just Int.
I believe all the solutions given so far work differently than Data.List.delete, which only deletes the first member.
deleteFromList x xs =
case break (==x) xs of
(_,[]) -> xs
(notsat,sat) -> notsat ++ tail sat
was my attempt to delete only the first member (haven't peaked at D.L yet).
It's unclear which behavior the top poster wants.