Substitue String in Haskell - haskell

Evening,
This is my attempt at an equivalent of "str_replace" in Haskell
strReplace :: (Char, Char) -> String -> String -> String {- Original (y) Parsed (z) -}
strReplace _ "" y = y
strReplace x y z = if (y !! 0) == fst x then strReplace x (drop 1 y) (z:([snd x])) else strReplace x (drop 1 y) (z:(y!!0))
Essentially, the first Tuple is the char to be substitued (Ie ('A', 'B') replaces all As to Bs, the second parameter is the String to be parsed and the third parameter should always be left an empty string. Compiler returns
*** Expression : z : [snd x]
*** Term : z
*** Type : [Char]
*** Does not match : Char
Ideas? :)

The problem with your code is that z : [snd x] is incorrect, z is a list but : wants it to be an element. This can be fixed by using z ++ [snd x].
If seeing the type signatures helps
(:) :: a -> [a] -> [a]
(++) :: [a] -> [a] -> [a]
Or in your specific case
(:) :: Char -> String -> String
(++) :: String -> String -> String
If I may suggest a few improvements to your code however, first strReplace shouldn't force you to pass an empty string
strReplace :: (Char, Char) -> String -> String
Next, we can do this two ways, using higher order functions or explicit recursion.
-- recursion
strReplace _ "" = "" -- Base case
strReplace (a, b) (c:cs) | a == c = b : strReplace (a,b) cs
| otherwise = c : strReplace (a, b) cs
So here if the string is empty we're done, otherwise we pattern match, if the first character is the one to be replaced, we replace it and recurse, otherwise we don't replace it and recurse.
This can actually be done much more cleanly with map though
strReplace (a, b) s = map (\c -> if c == a then b else c) s
This works identically to our previous version, but map abstracts out the looping logic.

z is of type [Char]. You can't use : to cons a [Char] into a [Char] - look at the type signature for :. You would have to use ++ to append one [Char] to another.
Additional points:
It would be better style for strReplace to have a signature :: Char -> Char -> String -> String -> String.
It would be even better style for the signature to be :: a -> a -> [a] -> [a] -> [a]
You shouldn't require the calling code to pass in an empty string. What if they don't - how will they know they made an error? If your recursive call requires it, use an inner function (using let or where).
Where you see a function that looks like foo x y = if (y == ... ) ... else ... it can almost always be improved by either pattern matching or guards.
To expand on point 4, you could rewrite that third line as
strReplace x y z | y !! 0 == fst x = ...
| otherwise = ...
Even better, if you took my advice in point 1 and split the tuple into two simple Char parameters, you could do this:
strReplace x1 x2 y#(y1:ys) z | x1 == y = ...
| otherwise = ...

Related

how to find a certain char in string and replacing it with the spaces, left in that string with anonymous function in haskell

I need to write a function ,with the use of foldl, which recieves a string "str"
and returns an anonymous function. The anonymous functions receives a char 'c' and exchanges every instance of 'c' in "str" with the remaining number of chars in the string "str"
speak :: String -> (Char -> String)
example:
"Hello" 'e' -> "H3llo"
"gate" 't' -> "ga1e"
I've tried this code, but cant get it to work properly:
speak :: String -> (Char ->String)
speak str = foldl (\x -> if x == str then x = show(length str) else str) str
You can not assign a value to x What you need to do is either return show (length xs) ++ xs in case the character x is the same as the one you are looking for, or x:xs (so a normal prepend of x to xs) in case it does not match. Your speak also has a Char as first parameter, and then converts a String to a String, so:
speak :: Char -> String -> String
speak c = foldr (\x xs -> if c == x then show (length xs) ++ xs else (x:xs))
or with swapped parameters:
speak :: String -> Char -> String
speak str c = foldr (\x xs -> if c == x then show (length xs) ++ xs else (x:xs)) str

Is there an easier way to write this function and only using prelude from Haskell?

So I'm pretty new to Haskell, and are trying to solve an assignment, I've solved it, but I'm wondering if there is an easier or prettier way to make a function do the same as my wordChange. I'm trying to only use what is already in prelude.
dictionaryChecker _ [] = False
dictionaryChecker word (x:xs) = if elem word (snd x) then True else dictionaryChecker word xs
wordChange :: String -> String
wordChange str = unwords (map (\s -> if length (translate s) > 0 then (translate s)
else if (dictionaryChecker s dictionary) then concat (replicate (length s) "*")
else s) (words str))
translate :: String -> String
translate str = contains str dictionary
contains _ [] = ""
contains str (x:xs) = if elem str (snd x) then fst x else contains str xs
I'd suggest to use the lookup function from Prelude, which takes a key and a list of tuples (a.k.a a dictionary) and returns Maybe value. This simplfies your function a lot. Also, if changeWord uses a dictionary, it should be explicit instead of using a global variable. Below, a partial solution: since it is an assignment I think you should try to complete it ;)
changeWord :: [(String, String)] -> String -> String
changeWord dic s = unwords $ substitute ws
where -- ws is just the list of words s has
ws = words s
-- the function substitute does the word changing recursively. Try to complete it
substitute [] = []
substitute (x:xs) =
case lookup x dic of -- look for x in the dictionary and returns the value if found
Nothing -> undefined --complete
Just y -> undefined --complete
An obfuscated answer: earn a gold star from your professor if you can explain how it works, and be accused of copying from the internet if you can't:
wordChange :: [(String, String)] -> String -> String
wordChange dict = unwords . map (foldr const <*> (`lookup` dict)) . words
Your dictionaryChecker is in essence an any :: Foldable f => (a -> Bool) -> f a -> Bool with elem word . snd as condition:
dictionaryChecker :: (Foldable f, Foldable g, Eq a) => a -> f (b, g a) -> Bool
dictionaryChecker word = any (elem word . snd)
as for a translate, we can work with a section of an infix operator [Haskell-wiki] to make a point-free function:
translate :: String -> String
translate = (`contains` dictionary)
and for contains we can work with a foldr :: Foldable f => (a -> b -> b) -> b -> f a -> b
contains :: (Foldable f, Foldable g, Eq a) => a -> f (String, g a) -> String
contains str = foldr (\x y -> if … then … else …) ""
I leave implementing the … parts as an exercise.

higher order function haskell

I'm new to Haskell, I've to do a function that counts the number of vowels in a string using the higher order function foldr
I've tried to create this function
vowels [] = 0
vowels (x:xs)= if elem x "aeiou" then 1 + vowels xs else vowels xs
But it doesn't work and I'm not able to do it using foldr, any suggestion?
Well a foldr :: (a -> b -> b) -> b -> [a] -> b is a function where the first parameter is a function f :: a -> b -> b. You can here see the a parameter as the "head" of the list, the second parameter b as the result of the recursion with foldr, and you thus want to produce a result in terms of these two for the entire function. This logic is basically encapsulated in the second clause of your function.
Indeed:
vowels (x:xs) = if elem x "aeiou" then 1 + vowels xs else vowels xs
can be rewritten as:
vowels (x:xs) = if elem x "aeiou" then 1 + rec else rec
where rec = vowels xs
and rec is thus the outcome of the recursive call, the second parameter of the "fold"-function. x on the other hand is the first parameter of the "fold"-function. We thus need to write this function, only in terms of x and rec, and this is simply:
\x rec -> if elem x "aeiou" then 1 + rec else rec
Furthermore we need to handle the case of an empty list, this is the first clause of your function. In that case the result is 0, this is the second paramter of the foldr, so we got:
vowels = foldr (\x rec -> if elem x "aeiou" then 1 + rec else rec) 0
Or a more clean syntax:
vowels = foldr f 0
where f x rec | elem x "aeiou" = 1 + rec
| otherwise = rec
We can further clean it up, by abstracting away rec:
vowels = foldr f 0
where f x | elem x "aeiou" = (1+)
| otherwise = id
You need to take a look at foldr's signature.
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
Never mind the Foldable part and focus on the first function it takes.
(a -> b -> b) b is the same type that you are supposed to return, so directly translating the signature into a lambda gives you \x acc -> acc, but you want to do more than just ignore every element.
Take a look at your function if elem x "aeiou" then 1 + vowels xs else vowels xs. You need to return b, not recurse adding one to it.
if elem x "aeiou" this part is fine. then 1 + acc <- see what I'm doing here? I'm adding one to the accumulator, not recursing manually, that is done by foldr, as for the else case: acc. That's it. You don't need to even touch x.
Putting it all together: vowels = foldr (\x acc -> if elem x "aeiou" then 1 + acc else acc) 0
The 0 is what the acc will start as.
If you want to know more about folds, I suggest you reimplement them yourself.
The easiest way to write something like that is to let the compiler guide you.
First, look only at the obvious parts of the foldr signature. This is the traditional signature, specialised to lists. Nowedays, foldr can actually work on any other suitable container as well, but this isn't important here.
foldr :: (a -> b -> b) -- ^ Not obvious
-> b -- ^ Not obvious
-> [a] -- ^ A list... that'll be the input string
-> b -- ^ Final result, so nothing to be done here.
So, your implementation will be of the form
vowels :: String -> Int
vowels s = foldr _ _ s
where we yet need to find out what to put in the _ gaps. The compiler will give you useful hints as to this:
$ ghc wtmpf-file6869.hs
[1 of 1] Compiling Main ( wtmpf-file6869.hs, wtmpf-file6869.o )
/tmp/wtmpf-file6869.hs:2:18: error:
• Found hole: _ :: Char -> Int -> Int
• In the first argument of ‘foldr’, namely ‘_’
In the expression: foldr _ _ s
In an equation for ‘Main.vowels’: Main.vowels s = foldr _ _ s
• Relevant bindings include
s :: String (bound at /tmp/wtmpf-file6869.hs:2:8)
vowels :: String -> Int (bound at /tmp/wtmpf-file6869.hs:2:1)
|
2 | vowels s = foldr _ _ s
| ^
So, a function that merely takes a single character, and then modifies an integer. That was actually already part of your original implementation:
vowels (x:xs) = if elem x "aeiou" then 1 + vowels xs else vowels xs
The bold part is essentially a function of a single character, that yields a number-modifier. So we can put that in the foldr implementation, using lambda syntax:
vowels s = foldr (\x -> if x`elem`"aeiou" then (1+) else _) _ s
I had to put the 1+ in parenthesis so it works without an explicit argument, as an operator section.
Ok, more gaps:
• Found hole: _ :: Int -> Int
• In the expression: _
In the expression: if x `elem` "aeiou" then (1 +) else _
In the first argument of ‘foldr’, namely
‘(\ x -> if x `elem` "aeiou" then (1 +) else _)’
• Relevant bindings include
x :: Char (bound at wtmpf-file6869.hs:2:20)
s :: String (bound at wtmpf-file6869.hs:2:8)
vowels :: String -> Int (bound at wtmpf-file6869.hs:2:1)
|
2 | vowels s = foldr (\x -> if x`elem`"aeiou" then (1+) else _) _ s
| ^
So that's the modifier that should take action when you've found a non-vowel. What do you want to modify in this case? Well, nothing actually: the count should stay as-is. That's accomplished by the id function.
vowels s = foldr (\x -> if x`elem`"aeiou" then (1+) else id) _ s
• Found hole: _ :: Int
• In the second argument of ‘foldr’, namely ‘_’
In the expression:
foldr (\ x -> if x `elem` "aeiou" then (1 +) else id) _ s
In an equation for ‘vowels’:
vowels s
= foldr (\ x -> if x `elem` "aeiou" then (1 +) else id) _ s
• Relevant bindings include
s :: String (bound at wtmpf-file6869.hs:2:8)
vowels :: String -> Int (bound at wtmpf-file6869.hs:2:1)
|
2 | vowels s = foldr (\x -> if x`elem`"aeiou" then (1+) else id) _ s
| ^
So that's an integer that's completely outside of the foldr. I.e. it can't depend on the string. In particular, it will also be used if the string is empty. Can only be 0!
vowels s = foldr (\x -> if x`elem`"aeiou" then (1+) else id) 0 s
No more gaps, so the compiler will just accept this. Test it:
$ ghci wtmpf-file6869
GHCi, version 8.2.1: http://www.haskell.org/ghc/ :? for help
Loaded GHCi configuration from /home/sagemuej/.ghc/ghci.conf
Loaded GHCi configuration from /home/sagemuej/.ghci
[1 of 1] Compiling Main ( wtmpf-file6869.hs, interpreted )
Ok, 1 module loaded.
*Main> vowels "uwkaefdohinurheoi"
9
Your definition can be tweaked into
vowels [] = 0
vowels (x:xs) = g x (vowels xs)
where
g x rec = if elem x "aeiou" then 1 + rec else rec
which matches the pattern
foldr r z [] = z
foldr r z (x:xs) = r x (foldr r z xs)
if we have foldr r z = vowels and r = g, and also z = 0.
That "pattern" is in fact a valid definition of the foldr function.
Thus we indeed have
vowels xs = foldr g 0 xs
where
g x rec = if elem x "aeiou" then 1 + rec else rec

Printing a field with additional dots in haskell

I'm writing a function called printField. This function takes an int and a string as arguments and then then prints a field like this "Derp..." with this: printField 7 "Derp". When the field consists of digits the output should be "...3456".
The function I wrote looks like this:
printField :: Int -> String -> String
printField x y = if isDigit y
then concat(replicate n ".") ++ y
else y ++ concat(replicate n ".")
where n = x - length y
This obviously isn't working. The error I get from GHC is:
Couldn't match type `[Char]' with `Char'
Expected type: Char
Actual type: String
In the first argument of `isDigit', namely `y'
In the expression: isDigit y
In the expression:
if isDigit y then
concat (replicate n ".") ++ y
else
y ++ concat (replicate n ".")
I can't get it to work :(. Can anyone help me out? Please keep in mind that I'm new to Haskell and functional programming in general.
isDigit :: Char -> Bool
in printField x y we have that y :: [Char] so you want to know if every Char is a digit (making a number). We use all isDigit y
Also, you did concat(replicate n "."),
we have "." :: [Char] and replicate :: Int -> a -> [a]
so replicate 2 "." :: [[Char]].
Just use '.' :: Char
The final code would be
import Data.Char
printField :: Int -> String -> String
printField x y = if all isDigit y
then (replicate n '.') ++ y
else y ++ (replicate n '.')
where n = x - length y
Could make it prettier
import Data.Char
printField :: Int -> String -> String
printField x y = if all isDigit y
then dots ++ y
else y ++ dots
where
dots = replicate n '.'
n = x - length y
Two things: first, you don't need the calls to concat. Second, you probably want to say something like if all isDigit y -- isDigit is of type Char -> Bool and y is a String, i.e. [Char], so you need to do something to make a function of type String -> Bool. The all function from the Prelude takes a function of type a -> Bool and returns a function of type [a] -> Bool that returns True if all the elements of the list you pass it satisfy your predicate.

Identifying repeating consecutive digits on the end of a String in haskell

So I write a function with the definition
getLastDigits :: String -> String
which finds repeating digits on the end of a String
So, for example.
getLastDigits "1000" should give "000"
getLastDigits "19990299" should give "99"
Coming from a java background I'm not quite sure how to structure this program. I'm thinking of using foldr but I'm fairly sure I can't stop the fold half way when the repeating digits end.
-edit solved. Use the group function.
Okay then, if it is not homework:
lastDigits :: String -> String
lastDigits s = firstDigits . reverse $ s
where firstDigits :: String -> String
firstDigits (x:xs) = x : takeWhile (== x) xs
firstDigits [] = []
import Data.Char (isDigit)
getLastTheSame :: Eq a => (a -> Bool) -> [a] -> [a]
getLastTheSame pred xs = f (reverse xs)
where f (y : ys) | pred y = y : takeWhile (== y) ys
f _ = []
lastDigits :: String -> String
lastDigits = getLastTheSame isDigit
You say you want repeating digits from the end of the string. I presume that if the last character is not a digit then you want the empty string returned.
Recall that type String = [Char].

Resources