Haskell Replace a value in a list with another value - haskell

I am pretty new to Haskell. I am trying to write a program that takes two values and a list and replaces every instance of the first value in the list with the second. E.g. repOcc 'n' 'i' "pink" would return "piik".
The following is my code:
repOcc :: t -> t -> [t] -> [t]
repOcc x y (z:zs) = if z == x
then z = y
subst x y zs
else subst x y zs
The error I am receiving at compile time is:
rev.hs:3 :32: error:
parse error on input '='
Perhaps you need a 'let' in a 'do' block?
e.g. 'let x = 5' instead of 'x = 5'
Failed, modules loaded: none.

Your program looks rather "imperative" whereas Haskell aims to be more "declarative". So you can not set a variable z in a list: once a list is constructed, you can not alter it anymore. So you have to construct a new list where elements that are equal to x are set to y.
Next you make use of the (==) function. That function is defined in the Eq typeclass, so you need to add Eq t as a type constraint to the signature.
So now we can start to construct such function. Usually when working with a list, we make use of recursion. The base case of the recursion is usually the empty list. In case we encounter the empty list, we should return an empty list, regardless what x and y are. So we use underscores as "don't care" patterns, and use [] as the list pattern, and write:
repOcc _ _ [] = []
The recursive case is when the list contains a head h and a tail t in a (h:t) pattern. In that case we check whether h is equal to x. In case it is, we construct a list with y as head, otherwise h is still the head.
repOcc x y (h:t) | x == h = y : tl
| otherwise = h : tl
Now the question remains what the tail of the result list tl should be. Here we use recursion, so we call repOcc with x y t:
where tl = repOcc x y t
Or putting it together:
repOcc :: Eq t => t -> t -> [t] -> [t]
repOcc _ _ [] = []
repOcc x y (h:t) | x == h = y : tl
| otherwise = h : tl
where tl = repOcc x y t
We can write such recursive functions, but the above is actually a special case of a map function: we map every character in such way that we check whether it is equal to x and if it is, we return y, otherwise we return h. So we can rewrite the above as:
repOcc :: Eq t => t -> t -> [t] -> [t]
repOcc x y ls = map (\h -> if h == x then y else h) ls
We can further improve the code by making use of eta-reduction:
repOcc :: Eq t => t -> t -> [t] -> [t]
repOcc x y = map (\h -> if h == x then y else h)

Related

Haskell Function

I need to create a function that takes string and decoding rules. It is supposed to change characters in string until there is nothing possible to change according to decoding rules.
Each time I get string and decoding rules (first is what change, second is to what).
I'm quite lost, I tried to create all possible combinations and then generate list based on rules. Here's my try.
rules = [('E',"GZ"),('F',"HK"),('C',"EF"),('J',"CC")]
string = "JCEJ"
combinations = [(x,y,z) | x <- [ch | ch <- string], y <- [x | (x,y) <- rules], z <- [y | (x,y) <- rules]]
generate = [z | (x,y,z) <- combinations, if x == y then z else x]
Error message:
decoder.hs:8:57: error:
• Couldn't match expected type ‘Bool’ with actual type ‘[Char]’
• In the expression: z
In the expression: if x == y then z else x
In a stmt of a list comprehension: if x == y then z else x
|
8 | generate = [z | (x,y,z) <- combinations, if x == y then z else x]
| ^
decoder.hs:8:64: error:
• Couldn't match expected type ‘Bool’ with actual type ‘Char’
• In the expression: x
In the expression: if x == y then z else x
In a stmt of a list comprehension: if x == y then z else x
|
8 | generate = [z | (x,y,z) <- combinations, if x == y then z else x]
| ^
Disclaimer: none of this is as pretty as it could be.
You have a lookup table with rules. Haskell has a handy lookup function:
ghci> :t lookup
lookup :: Eq a => a -> [(a, b)] -> Maybe b
We can fold a lookup over the string:
ghci> foldr (\x i -> case lookup x rules of {Just s -> s ++ i; _ -> (x:i)}) "" "EF"
"GZHK"
Let's call this singlePassDecode:
singlePassDecode :: Foldable t => t Char -> [(Char, [Char])] -> [Char]
singlePassDecode s rules = foldr update "" s
where
update ch acc =
case lookup ch rules of
Just s' -> s' ++ acc
Nothing -> ch : ""
But a single pass doesn't necessarily get the job done. We need to recursively call this until there are no transformations left to perform. This means we need to know if any of the characters in the input string are in the lookup table.
The ... is left to fill in with the correct recursive call to avoid presenting a complete answer.
decode :: [Char] -> [(Char, [Char])] -> [Char]
decode s rules
| any (\ch -> elem ch (map fst rules)) s = ...
| otherwise = s
The first condition might also be expressed as follows.
any (flip elem $ map fst rules) s
A String is a list of Chars, so the [ch | ch <- string] is not necessary.
You here defined some inner list comprehensions with x, but that x is a more locally scoped variable, not the x as the x in x <- [ ch | ch <- str].
You can make a filter condition to filter, so:
generate = concat [ y | x <- string, (x', y) <- rules, … ]
Here the … is a part that you will need to fill in. It will need to compare x with x'.
Your list of rules describes a mapping from one Char to either two Chars (if there is a match) or one Char (the original input, if there is no match). We can handle both of those cases by always returning a [Char], and we can generalize to any a rather than being specific to Char:
import Data.Maybe (fromMaybe)
transform :: Eq a => [(a, [a])] -> a -> [a]
transform rules x = fromMaybe [x] (lookup x rules)
Since this mapping depends on no other context, concatMap (also spelled (>>=)) is a great tool for applying it across a list of inputs and concatenating the results.
transformAll :: Eq a => [(a, [a])] -> [a] -> [a]
transformAll rules = concatMap (transform rules)
-- or, transformAll = concatMap . transform
It will also be useful to have a function that applies a function repeatedly until it results in no change:
fixPoint :: Eq a => (a -> a) -> a -> a
fixPoint f x | x == x' = x
| otherwise = fixPoint f x'
where x' = f x
Then all that's left is to combine our tools:
transformStringRepeatedly :: Eq a => [(a, [a])] -> [a] -> [a]
transformStringRepeatedly rules = fixPoint (transformAll rules)
-- or, transformStringRepeatedly = fixPoint . transformAll
main = print (transformStringRepeatedly [('E',"GZ"),('F',"HK"),('C',"EF"),('J',"CC")] "JCEJ")
We can see that it produces the answer you expected:
$ runghc tmp.hs
"GZHKGZHKGZHKGZGZHKGZHK"

Haskell: Using multiple let and return a value after the do block inside a function

As the title says, i want to return a value after the do block.
Example: Writing a function that inserts a variable at a given position in an array:
insertAt :: a -> Int -> [a] -> [a]
insertAt x n xs = do
let before = take n xs
let after = drop n xs
let merged = before ++ [x] ++ after
in merged
For example:
insertAt 'x' 3 "Aleander" => "Alexander"
Anyway, when using a single let call, one could return a value using the in keyword, but with the multiple calls of let as in the example i get the error:
error: parse error on input `in'
I know i could do the whole thing in a single let usage, but i want to know how to deal with multiple let calls :)
Thanks for the help!
Please do not use a do expression. do expressions are snytactical sugar for expressions with binds. Yes a list is an instance of Monad, but you use it not in the correct way.
You can here define your lets in a block like:
insertAt :: a -> Int -> [a] -> [a]
insertAt x n xs =
let before = take n xs
after = drop n xs
merged = before ++ [x] ++ after
in merged
But it might be more elegant to use splitAt :: Int -> [a] -> ([a], [a]), like:
insertAt :: a -> Int -> [a] -> [a]
insertAt x n xs = let (hs,ts) = splitAt n xs in hs ++ x : ts
If you want to use the list monad, though, you can write
insertAt x n ys = do
(i, y) <- zip [0..] ys
if i == n then [x, y] else [y]
This works by "numbering" each element of the input list. Normally, you just return each element as you find it (remember, return x == [x] in the list monad). But at position n, you want to "sneak in" x before the current element of the existing list. You do that by providing the list [x,y] instead of simply [y].
insertAt 'x' 3 "Aleander" essentially becomes concat ["A", "l", "e", "xa", "n", "d", "e", "r"].
Drawback: it won't append 'x' to the output, regardless of what n you supply, so this may be more of a demonstration of the list monad than a solution to your actual problem. One simple fix is to special case n == 0, then insert x after the n the element. This lets you append if n == length ys, but not n > length ys:
insertAt x n ys = if n == 0 then x:ys else do
(i, y) <- zip [1..] ys -- Note the increase in indices
if i == n then [y, x] else [y]

Haskell: cannot construct the infinite type

I got a list of tuples (day:month) and want to find the month with the biggest amount of days.
I made a function that accepts my list of tuples and list of months (or just 1) to check and returns the maximum amount of dates in one month in specified period
maxweekends x [] = 0
maxweekends x [n] = length (filter ((==n).snd) x)
maxweekends x (y:ys) = max (maxweekends x [y]) (maxweekends x ys)
Then I wrote some simple function to use it, but I cant compile it because of "cannot construct the infinite type" error. I already spent a few hours with this error but I just cant understand what is wrong.
func x [] = 0
func x (y:ys)
| maxweekends x y < maxweekends x ys = func x ys
| otherwise = y
In theory it should call itself until there is no month with bigger amount of dates and then just return answer.
Thanks.
Edit: here is traceback of error
Your infinite type arises from the fact that you call maxweekends with x y and x ys. Since the type of maxweekends :: Eq b => [(a, b)] -> [b] -> Int specifies that given the "second" parameter is of type [b], then the first parameter is a type of [(a, b)], this means that x should be [(a, b)] (for the first call) and [(a, [b])] (for the second call) at the same time, which is impossible.
I think it might be better to first restructure this. Let us first construct a function that looks like:
groupLength :: Eq b => Int -> b -> [(a, b)] -> Int
groupLength d _ [] = d
groupLength _ x ys = length (filter ((x==) . snd) ys)
This will thus for a given "month" x obtain the number of elements in the list with as second item of the tuple that "month".
Now we can generate an "argmax" that calculates for which x, f x produces a maximum value:
argmax :: Ord b => (a -> b) -> [a] -> Maybe (a, b)
argmax _ [] = Nothing
argmax f (x:xs) = Just (go x (f x) xs)
where go x y [] = (x, y)
go x y (x2:xs) | y <= y2 = go x y xs
| otherwise = go x2 y2 xs
where y2 = f x2
So now it is only a matter of combining the the groupLength (which is an abstract version of your maxweekends with argmax (which is more or less what your func is after). I leave this as an exercise.

Get positions of elements in list of strings in Haskell

my title might be a bit off and i'll try to explain a bit better what i'm trying to achieve.
Basically let's say i have a list:
["1234x4","253x4",2839",2845"]
Now i'd like to add all the positions of the strings which contain element 5 to a new list. On a current example the result list would be:
[1,3]
For that i've done similar function for elem:
myElem [] _ = False
myElem [x] number =
if (firstCheck x) then if digitToInt(x) == number then True else False else False
myElem (x:xs) number =
if (firstCheck x) then (if digitToInt(x) == number then True else myElem xs number) else myElem xs number
where firstCheck x checks that the checked element isn't 'x' or '#'
Now in my current function i get the first element position which contains the element, however my head is stuck around on how to get the full list:
findBlock (x:xs) number arv =
if myElem x number then arv else findBlock xs number arv+1
Where arv is 0 and number is the number i'm looking for.
For example on input:
findBlock ["1234x4","253x4",2839",2845"] 5 0
The result would be 1
Any help would be appreciated.
The function you want already exists in the Data.List module, by the name of findIndices. You can simply use (elem '5') as the predicate.
http://hackage.haskell.org/package/base-4.8.1.0/docs/Data-List.html#v:findIndices
If, for some reason, you're not allowed to use the built-in one, it comes with a very pretty definition (although the one actually used has a more complicated, more efficient one):
findIndices p xs = [ i | (x,i) <- zip xs [0..], p x]
By the way, I found this function by searching Hoogle for the type [a] -> (a -> Bool) -> [Int], which (modulo parameter ordering) is obviously the type such a function must have. The best way to find out of Haskell has something is to think about the type it would need to have and search Hoogle or Hayoo for the type. Hoogle is better IMO because it does slightly fuzzy matching on the type; e.g. Hayoo wouldn't find the function here by the type I've given, because it take the arguments in the reverse order.
An implementation of findIndices, for instructional purposes:
findIndices ok list = f list 0 where
f [] _ = []
f (x:xs) ix
| ok x = ix : f xs (ix+1)
| otherwise = f xs (ix+1)
Use it like findIndices (elem '5') my_list_o_strings
You're trying to work your way through a list, keeping track of where you are in the list. The simplest function for doing this is
mapWithIndex :: (Int -> a -> b) -> [a] -> [b]
mapWithIndex = mwi 0 where
mwi i _f [] = i `seq` []
mwi i f (x:xs) = i `seq` f i x : mwi (i+1) f xs
This takes a function and a list, and applies the function to each index and element. So
mapWithIndex (\i x -> (i, x)) ['a', 'b', 'c'] =
[(0,'a'), (1,'b'),(2,'c')]
Once you've done that, you can filter the list to get just the pairs you want:
filter (elem '5' . snd)
and then map fst over it to get the list of indices.
A more integrated approach is to use foldrWithIndex.
foldrWithIndex :: (Int -> a -> b -> b) -> b -> [a] -> b
foldrWithIndex = fis 0 where
fis i _c n [] = i `seq` n
fis i c n (x:xs) = i `seq` c i x (fis (i+1) c n xs)
This lets you do everything in one step.
It turns out that you can implement foldrWithIndex using foldr pretty neatly, which makes it available for any Foldable container:
foldrWithIndex :: (Foldable f, Integral i) =>
(i -> a -> b -> b) -> b -> f a -> b
foldrWithIndex c n xs = foldr go (`seq` n) xs 0 where
go x r i = i `seq` c i x (r (i + 1))
Anyway,
findIndices p = foldrWithIndex go [] where
go i x r | p x = i : r
| otherwise = r

Having trouble with H-15

I am doing Problem 15. Which states:
(**) Replicate the elements of a list a given number of times.
Example:
* (repli '(a b c) 3)
(A A A B B B C C C)
Example in Haskell:
> repli "abc" 3
"aaabbbccc"
My plan was to do something like this:
repli :: [a] -> Integer -> [a]
repli [] y = []
repli (x:xs) y | appendNo x y == [] = repli(xs) y
| otherwise = appendNo x y : (x:xs)
where
appendNo :: a -> Integer -> [a]
appendNo a 0 = []
appendNo a y = a:appendNo a (y-1)
Where I would make a function called appendNo that returns a list of 1 element y times then append it to the original list. Then take the body of the list and repeat this process until there are no more body elements left. But, I get the error:
H15.hs:6:30:
Couldn't match type `a' with `[a]'
`a' is a rigid type variable bound by
the type signature for repli :: [a] -> Integer -> [a] at H15.hs:3:1
In the return type of a call of `appendNo'
In the first argument of `(:)', namely `appendNo x y'
In the expression: appendNo x y : (x : xs)
Failed, modules loaded: none.
6:30 is at the on the p in appendNo in this line:
| otherwise = appendNo x y : (x:xs)
Ok thanks dave4420 I was able to figure it out by doing:
repli :: [a] -> Integer -> [a]
repli [] y = []
repli (x:xs) y = appendNo x y ++ repli(xs) y
where
appendNo :: a -> Integer -> [a]
appendNo a 0 = []
appendNo a y = a:appendNo a (y-1)
| otherwise = appendNo x y : (x:xs)
There is a type error in this line. So ask yourself:
What is the type of appendNo x y?
What is the type of (x:xs)?
What is the type of (:)?
Then you should be able to see why they don't match up.
If you still can't see why they don't match up, ask yourself
What is the type of x?
What is the type of xs?
What is the type of (:)?
Bear in mind that this time the types do match up.
As the problem is solved, let me give you a hint: You should try to think in transformations, not in "loops". Start with some concrete values like n = 3 and list = "ABCD". Then you should think along the lines "I need every element three times". There is already a function for doing the replication, which is surprisingly called replicate. So the sentence can be translated to map (replicate 3) "ABCD", which gives you ["AAA","BBB","CCC","DDD"]. That's almost what you want, you just need to concat the elements. This gives:
repli list n = concat (map (replicate n) list)
Because this operation is very common, there is a concatMap function combining concat and map, as well as the operator (>>=) doing the same, just with flipped arguments. So a very short solution would be:
repli list n = list >>= replicate n
This can be translated to the do-notation or a list comprehension as well:
repli list n = do
x <- list
y <- replicate n x
return y
repli list n = [y | x <- list, y <- replicate n x]

Resources