Explaining Haskell Code [closed] - haskell

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
I was given this code and told to explain its logic, does anyone know what is going on, new to Haskell.
cpfx :: [[Char]] -> [Char]
cpfx [] = []
cpfx [x] = x
cpfx (x:xs) = cpfx' (x:xs) 0
cpfx' :: [[Char]] -> Int -> [Char]
cpfx' [x] _ = []
cpfx' (x:xs) n
| ifMatch (x:xs) n = x!!n : cpfx' (x:xs) (n+1)
| otherwise = []
ifMatch :: [[Char]] -> Int -> Bool
ifMatch [x] _ = True
ifMatch [x,y] n = x!!n == y!!n
ifMatch (x:y:xs) n
| x!!n == y!!n = ifMatch xs n
| otherwise = False
I am having trouble understand what cpfx,cpfx' and ifMatch are doing.

Take each function directly and look at it. Let's start from the bottom up, since they each use each other.
ifMatch :: [[Char]] -> Int -> Bool
so ifMatch takes a list of lists of Chars (or a list of Strings) and an Int, and gives you back a true/false value. Now let's look at the pattern matches
[x] _ = True -- one element list and any number is True
[x, y] n = x!!n == y!!n {- two element list is true if the character at index
#n# is the same in both lists -}
(x:y:xs) n -- three+ element list guards against...
| x!!n == y!n -- if the characters at index #n# are equal...
= ifMatch xs n -- ... then recurse
| otherwise = False -- otherwise, give me a False.
Taken together, you can see that ifMatch is supposed to check that all strings passed to it have the same letter at index n. It is equivalent to:
ifMatch [] _ = True
ifMatch xs n = let c = head xs !! n in
foldr ((&&) . (==c) . (!!n)) True xs
Though it actually appears to have a slight bug. It only checks if each pair of strings has identical letters at index n, so
ifMatch ["This", "That", "In", "Onward"] 1 == True
-- (^ == ^) && (^ == ^)
cpfx' :: [[Char]] -> Int -> [Char]
so cpfx' takes a list of list of Chars (or a list of Strings) and an Int and gives you back a list of Chars (or a String). Let's look at pattern matches here:
cpfx' [x] _ = [] -- one element list and any number is the empty list
cpfx' (x:xs) n -- multiple element list guards against...
| ifMatch (x:xs) n -- if all #x:xs# share an element at #n#...
= x!!n : -- ...add x!!n and...
cpfx' (x:xs) (n+1) -- ...recurse to the next index
| otherwise = [] -- otherwise, empty list.
So this grabs a character from x until the characters no longer match among all the strings in x:xs, and returns that prefix string.
cpfx :: [[Char]] -> [Char]
From a list of list of Chars (or a list of Strings) to a list of Chars (or a String).
cpfx [] = [] -- empty string gives empty string
cpfx [x] = x -- one-element list gives its only element
cpfx (x:xs) = cpfx' (x:xs) 0 {- anything else gives the shared prefix starting
at index zero -}
All in all, we've got three functions that work together to give you the longest substring of the head of a list of strings that exists at the beginning of all strings in that list.
import Data.Maybe (mapMaybe)
safeIndex :: [a] -> Int -> Maybe a
safeIndex xs n | length xs > n = Just $ xs !! n
| otherwise = Nothing
allEq :: Eq a => [a] -> Bool
allEq [] = True
allEq [_] = True
allEq (x:xs) = all (==x) xs
prefix :: [String] -> String
prefix xss#(x:_) = map snd $ takeWhile pred $ zip [0..] x where
pred :: (Int, Char) -> Bool
pred (n, _) = (allEq . mapMaybe (flip safeIndex n)) xss
testData :: [String]
testData = ["Hello", "Hello, World!", "Hello, Universe!", "Hello everybody!", "Hell's to you, then!"]
main :: IO ()
main = do
let p = prefix testData
putStrLn "Should be \"Hell\""
putStrLn p
or much easier with transpose
import Data.List (transpose)
prefix' :: [String] -> String
prefix' xss#(x:_) = take (length $ takeWhile allEq transposed) x where
transposed = transpose xss
or slightly more efficient
lengthWhile :: (a -> Bool) -> [a] -> Int
lengthWhile _ [] = 0
lengthWhile pred (x:xs) | pred x = 1 + lengthWhile pred xs
| otherwise = 0
prefix'' :: [String] -> String
prefix'' xss#(x:_) = take (lengthWhile allEq transposed) x where
transposed = transpose xss

Related

Haskell: compare sequences and count the length of the prefix that is common

Im new to haskell and Im writing a function that compares two sequences and reports the length of the prefix they have in common. This is what I have so far but it doesn't work for all cases.
commonLen :: Eq a => [a] -> [a] -> Int
commonLen (x:xs) [] = 0
commonLen (x:xs) (y:ys) | x==y = 1+(commonLen xs ys)
| otherwise = commonLen xs ys
Any ideas where im going wrong? Any help would be appreciated
You should not recurse in case x is different from y. In that case we return 0:
commonLen :: Eq a => [a] -> [a] -> Int
commonLen [] _ = 0
commonLen _ [] = 0
commonLen (x:xs) (y:ys) | x == y = 1 + commonLen xs ys
| otherwise = 0 -- ← return 0
You also can avoid the explicit recursion, and work with:
commonLen :: Eq a => [a] -> [a] -> Int
commonLen xs ys = length (takeWhile id (zipWith (==) xs ys))
here we iterate over both lists concurrently, and compare the elements. We thus make a list of Bools that is True if the elements of the two lists match. Then we use takeWhile to take elements as long as the item is True, and we use length to determine the number of elements in that list. Due to Haskell's laziness, we will never evaluate the entire list if one of the elements differs from the corresponding element in the other list.

Is there a way to get a 'split' function in Haskell to accept two different types of input?

I am trying to create a function split that can take either [Int] and Int or [Char] Char to split either a list of integers on an integer given or split a string on a character given. I.e.
Main> split [1,2,3,0,4,5,0,0,7,8,9] 0
[[1,2,3],[4,5],[7,8,9]]
Main> split "Mary had a little lamb" ' '
["Mary","had","a","little","lamb"]
I've tried using Either and (Eq a) but it still doesn't seem to work. Below is what I've tried doing using class instances but I know very little about this and get the error Haskell 98 does not support multiple parameter classes.
The best way I think I'd understand it would be to use pattern matching or list comprehensions. Any help much appreciated.
class Split a where
split :: (Eq a) => [a] -> a -> [a]
instance Split [Char] Char where
split [] c = [""]
split (x:xs) c
| x == c = "" : (split xs c)
| otherwise = (x : head (split xs c)) : tail (split xs c)
instance Split [Int] Int where
split [] n = []
split (x:xs) n
| x == n = [] : (split xs n)
| otherwise = (x : head (split xs n)) : tail (split xs n)
I can get the split function to work with strings and characters but not lists of integers.
You need a polymorphic function split
split :: (Eq a) => [a]->a->[[a]]
Implementation is simple
split [] _ = [[]]
split (x:xs) c
| x == c = [] : (split xs c)
| otherwise = (x : head subSplit) : tail subSplit
where
subSplit = split xs c
EDIT
I suggest different implementation.
split :: Eq a => [a] -> a -> [[a]]
split x c = map reverse $ split' x c []
where
split' :: Eq a => [a] -> a -> [a] -> [[a]]
split' [] _ a = [a]
split' (x:xs) c a
| x == c = a : split' xs c []
| otherwise = split' xs c (x:a)
Just to contribute with an other approach. This solution uses foldr. I think it is quite neat but less undestable than #talex's
split :: (Eq a) => [a] -> a -> [[a]]
split l c = foldr f acc l
where acc = [[]]
f a t#(i#(x:_):xs) = if a == c then []:t else (a:i):xs -- Case when the current accumulator is not empty
-- | |- cons a to current accumulator
-- |- start a new accumulator
f a t#([]:xs) = if a == c then t else [a]:xs -- Case when the current accumulator is empty. Usefull when two separators are together
-- | |- cons a to current accumulator
-- |- Don't start a new accumulator, just continue with the current
Just correct solution.
split :: Eq a => [a] -> a -> [[a]]
split xs delim = go $ dropWhile (== delim) xs
where
go [] = []
go xs = let (tok, rest) = break (== delim) xs
in tok : go (dropWhile (== delim) rest)
Data.List.Split.splitOn (available from the split package) is close:
> splitOn [0] [1,2,3,0,4,5,0,0,7,8,9]
[[1,2,3],[4,5],[],[7,8,9]]
> splitOn " " "Mary had a little lamb"
["Mary","had","a","little","lamb"]
Your split :: Eq a => [a] -> a -> [[a]] would be
split lst d = filter (not.null) $ splitOn [d] lst

Haskell type signatures with varying parameters

Project Euler #4: A palindromic number reads the same both ways. The largest palindrome made from the product of two 2-digit numbers is 9009 = 91 × 99. Find the largest palindrome made from the product of two 3-digit numbers.
This solution works:
p004largestPalindrome :: Integer
p004largestPalindrome = largest [ a * b | a <- [100..999], b <- [100..999], isPalindrome $ show(a*b) ]
where
isPalindrome [] = True
isPalindrome [_] = True
isPalindrome (x:xs) = if x == last xs then isPalindrome (init xs) else False
largest [] = 0
largest [x] = x
largest (x:xs) = if x > head xs then largest (x:(tail xs)) else largest xs
My question is: can you assign type signatures to the functions in the where clause, given that the both have different arrangements of parameters ([], [x], (x:xs))? Sticking in isPalindrome :: (Eq a) -> [a] -> Bool throws an error.
Edit: I am trying to insert a type signature like so:
p004largestPalindrome :: Integer
p004largestPalindrome = largest [ a * b | a <- [100..999], b <- [100..999], isPalindrome $ show(a*b) ]
where
isPalindrome :: (Eq a) -> [a] -> Bool
isPalindrome [] = True
isPalindrome [_] = True
isPalindrome (x:xs) = if x == last xs then isPalindrome (init xs) else False
largest [] = 0
largest [x] = x
largest (x:xs) = if x > head xs then largest (x:(tail xs)) else largest xs
You have a typo. [Should] be (Eq a) =>... (arrow should be made with equal sign) – Michal Seweryn
Class constraints are separated from the types they constrain with =>.

Haskell list: Replacing elements given their locations in the list

I'm fairly new to Haskell and trying to figure out how I would write a Function to do this and after combing Google for a few hours I'm at a loss on how to do it.
Given the following two lists in Haskell
[(500,False),(400,False),(952,True),(5,False),(42,False)]
[0,2,3]
How would I change the Boolean of the First list at each location given by the second list to a Value of True for an Output of
[(500,True),(400,False),(952,True),(5,True),(42,False)]
This is how I would do it (assumes the list of indexes to replace is sorted).
First we add an index list alongside the list of indexes to replace and the original list.
Then we recurse down the list and when we hit the next index to replace we replace the boolean and recurse on the tail of both all three lists. If this is not an index to
replace we recurse on the entire replacement index list and the tail of the other two lists.
setTrue :: [Int] -> [(a, Bool)] -> [(a, Bool)]
setTrue is xs = go is xs [0..] -- "Index" the list with a list starting at 0.
where
go [] xs _ = xs -- If we're out of indexes to replace return remaining list.
go _ [] _ = [] -- If we run out of list return the empty list.
go indexes#(i:is) (x:xs) (cur:cs)
| i == cur = (fst x, True) : go is xs cs -- At the next index to replace.
| otherwise = x : go indexes xs cs -- Otherwise, keep the current element.
This is basically the same as Andrew's approach, but it doesn't use an additional index list, and is a little bit more inspired by the traditional map. Note that unlike map, the provided function must be a -> a and cannot be a -> b.
restrictedMap :: (a -> a) -> [Int] -> [a] -> [a]
restrictedMap f is xs = go f is xs 0
where
go f [] xs _ = xs
go f _ [] _ = []
go f ind#(i:is) (x:xs) n
| i == n = f x : go f is xs (n+1)
| otherwise = x : go f ind xs (n+1)
setTrue = restrictedMap (\(x,_) -> (x, True))
Straightforward translation from the description will be:
setIndexTrue f a = [(x, p || i `elem` f) | (i, (x,p)) <- zip [0..] a]
Or using the fantastic lens library:
setTrue :: [(a,Bool)] -> Int -> [(a,Bool)]
setTrue xs i = xs & ix i . _2 .~ True
setTrues :: [(a,Bool)] -> [Int] -> [(a,Bool)]
setTrues = foldl setTrue
Since the approach I would use is not listed:
setTrue spots values = let
pattern n = replicate n False ++ [True] ++ Repeat False
toSet = foldl1 (zipWith (||)) $ map pattern spots
in zipWith (\s (v,o) -> (v, o || s)) toSet values

Haskell filter string with only the first occuring Char

I want to filter a string with a string.
What I want is to use delete every first occurring char.
myFunc :: String -> String -> String
Like:
myFunc "dddog" "bigdddddog" = "biddg"
In "dddog": 3x d, 1x o, 1x g
In the second string it removed 3x d, 1x o and 1x g
So the output: biddg
I can't use filter for it, because it will delete all occurring chars.
And I struggled a long time with it.
Thanks in advance:)
How about
Prelude> :m +Data.List
Prelude Data.List> "bigdddddog" \\ "dddog"
"biddg"
Not the nicest solution, but you can understand easier what's going on:
myfunc :: String -> String -> String
myfunc [] xs = xs
myfunc (x:xs) ys = myfunc xs $ remove x ys
where
remove _ [] = []
remove x (y:ys) = if x == y then ys else y : remove x ys
As you commented, you want to use guards. Do you mean this?
myfunc :: String -> String -> String
myfunc [] xs = xs
myfunc (x:xs) ys = myfunc xs $ remove x ys
remove :: Char -> String -> String
remove _ [] = []
remove x (y:ys)
| x == y = ys
| otherwise = y : remove x ys
some of the other solutions don't seem to produce the same result you posted. I think I have a simple solution that does what you asked for but I may be misunderstanding what you want. All I do in the following code is go though the list and apply 'delete' to every element in the list. It's not exactly efficient but it gets the job done.
import Data.List
myFunc (x:xs) ys = myFunc xs (delete x ys)
myFunc [] ys = ys
There are perhaps more efficient solutions like storing the "to remove" list in a tree with the number of occurences stored as the value then traversing the main list testing to see if the count at that key was still greater than zero. I think that would give you O(n*lg(m)) (where n is the size of the list to be removed from and m is the size of the "to remove" list) rather than O(n*m) as is the case above. This version could also be maid to be lazy I think.
edit:
Here is the tree version I was talking abut using Data.Map. It's a bit complex but should be more efficient for large lists and it is somewhat lazy
myFunc l ys = myFunc' (makeCount l) ys
where makeCount xs = foldr increment (Map.fromList []) xs
increment x a = Map.insertWith (+) x 1 a
decrement x a = Map.insertWith (flip (-)) x 1 a
getCount x a = case Map.lookup x a of
Just c -> c
Nothing -> 0
myFunc' counts (x:xs) = if (getCount x counts) > 0
then myFunc' (decrement x counts) xs
else x : myFunc' counts xs
myFunc' _ [] = []
I am not quite sure about how you want your function to behave, how about this?
import Data.List (isPrefixOf)
myFunc :: String -> String -> String
myFunc _ [] = []
myFunc y x'#(x:xs) | y `isPrefixOf` x' = drop (length y) x'
| otherwise = x : myFilter xs y
This gives the following output in GHCi:
> myFunc "dddog" "bigdddddog"
> "bigdd"
If this is not what you had in mind, please give another input/output example.
I like kaan's elegant solution. In case you meant this...here's one where the "ddd" would only be removed if matched as a whole:
import Data.List (group,isPrefixOf,delete)
f needles str = g (group needles) str where
g needles [] = []
g needles xxs#(x:xs)
| null needle' = [x] ++ g needles xs
| otherwise = let needle = head needle'
in g (delete needle needles) (drop (length needle) xxs)
where needle' = dropWhile (not . flip isPrefixOf xxs) needles
Output:
*Main> f "dddog" "bigdddddog"
"biddg"
*Main> f "dddog" "bdigdogd"
"bdidgd"
No monadic solution yet, there you go:
import Control.Monad.State
myFunc :: String -> State String String
myFunc [] = return ""
myFunc (x:xs) = get >>= f where
f [] = return (x:xs)
f (y:ys) = if y == x then put ys >> myFunc xs
else myFunc xs >>= return . (x:)
main = do
let (a,b) = runState (myFunc "bigdddddog") "dddog" in
putStr a
Using predefined functions from Data.List,
-- mapAccumL :: (acc -> x -> (acc, y)) -> acc -> [x] -> (acc, [y])
-- lookup :: (Eq a) => a -> [(a, b)] -> Maybe b
{-# LANGUAGE PatternGuards #-}
import Data.List
picks [] = [] -- http://stackoverflow.com/a/9889702/849891
picks (x:xs) = (x,xs) : [ (y,x:ys) | (y,ys) <- picks xs]
myFunc a b = concat . snd $ mapAccumL f (picks a) b
where
f acc x | Just r <- lookup x acc = (picks r,[])
f acc x = (acc,[x])
Testing:
Prelude Data.List> myFunc "dddog" "bigdddddog"
"biddg"
edit: this is of course a bit more complex than (\\). I'll let it stand as an illustration. There could be some merit to it still, as it doesn't copy the 2nd (longer?) string over and over, for each non-matching character from the 1st (shorter) string, as delete apparently does, used in (\\) = foldl (flip delete).

Resources