I have function change which replace some characters to numbers. Here it is:
change [] = []
change (x:xs) | x == 'A' = '9':'9':change xs
| x == 'B' = '9':'8':change xs
| otherwise = change xs
and the output is:
Main> change "aAB11s"
"9998"
but I need this:
Main> change "aAB11s"
"a999811s"
How can I do this?
Try this:
change [] = []
change (x:xs) | x == 'A' = '9':'9':change xs
| x == 'B' = '9':'8':change xs
| otherwise = x:change xs
The only change is in otherwise.
In addition to #kostya 's answer, you don't need to write the recursive part youself, try this out:
change :: String -> String
change xs = concatMap chToStr xs
where chToStr 'A' = "99"
chToStr 'B' = "98"
chToStr x = [x]
or, more point-freely (actually this is preferred if the point-free refactoring doesn't hurt the readability):
change :: String -> String
change = concatMap chToStr
where chToStr 'A' = "99"
chToStr 'B' = "98"
chToStr x = [x]
And you can test the result:
λ> change "aAB11s"
"a999811s"
Some explanation:
It's tempting to do an elementwise replacement by passing map a function
f :: Char -> Char. But here you can't do that because for A, you want two characters, i.e. 99, so the function you want is of type Char -> String (String and [Char] in Haskell are equivalent) which does not fit the type signature.
So the solution is to also wrap other characters we don't care about into lists, and afterwards, we can perform a string concatenation(this function in Haskell is called concat) to get a string back.
Further, concatMap f xs is just a shorthand for concat (map f xs)
λ> map (\x -> [x,x]) [1..10]
[[1,1],[2,2],[3,3],[4,4],[5,5],[6,6],[7,7],[8,8],[9,9],[10,10]]
λ> concat (map (\x -> [x,x]) [1..10])
[1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10]
λ> concatMap (\x -> [x,x]) [1..10]
[1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10]
Related
This is what I have so far:
toStr :: (Num a) => [a] -> String
toStr (x:xs)
| length xs == 0 = []
| length xs > 0 = show x : toStr xs
I keep getting this error:
* Couldn't match type `Char' with `[Char]'
Expected type: [String]
Actual type: String
I don't understand why it is receiving a Char instead of a [Char]. Thanks in advance.
For help, the thing I am trying to do is convert a binary list [1, 0, 0, 1, 1, 0] to a list like this "100110".
Understanding the Problem
toStr :: (Num a) => [a] -> String
toStr (x:xs)
| length xs == 0 = []
| length xs > 0 = show x : toStr xs
^ ^ ^
This is a String | |
| This is a String
This is a function of type String -> [String] -> [String]
So you have:
show x which is a String
toStr xs which is a String
The function : which expects a String and [String].
That disagreement on toStr xs is a String but was expected, by : to be a list of Strings is the crux of the problem. You wanted to concatenate your strings together into a single string (show x ++ toStr xs).
Understanding the Next Problem*
Now you should have a few other issues. First, you have an Num a => a that you try to show. The function show is not part of Num but instead part of the Show class so change Num a => to Show a =>.
Finally, this code doesn't handle the empty list case very well:
toStr (x:xs)
| length xs == 0 = []
Noticing nothing comes after x this code will ignore the last value, x, and return the empty list. It doesn't handle the case where there is no "last element" and all you have is the empty list. To handle that try toStr [] = [].
Putting it Together
toStr :: (Show a) => [a] -> String
toStr [] = []
toStr (x:xs) = show x ++ toStr xs
with a result of:
> toStr [1,0,0,1,1]
"10011"
Idiomatic Code
The above is a fine result but manual primitive recursive functions aren't usually necessary when writing Haskell. Most operations are a type of map or fold over the data. In this case it is a mapping of the show function (notice how it is showing every element) and a fold of the ++ function - also known as string concatenation.
toStr2 xs = foldr (++) "" (map show xs)
-- > toStr2 [1,0,0,1,1]
-- "10011"
Even this can be further simplified. It is so common there exists a special function concatMap:
toStr3 xs = concatMap show xs
Which we can "eta reduce" (remove the outer most arguments of the function definition/application - think in terms of defining a function as another function and not the values it produced):
toStr4 = concatMap show
Alternatively, we could re-gain the original verb-age of fold and map. The concatMap function is just a specific type of fold + map that works over lists. There is a more general foldMap that works with any function that produces a monoid (and lists are one such structure, which means that so are Strings since they are lists of characters):
toStr5 = foldMap show
As mentioned by Sergey, the ':' operator cannot be used, because the expression "show x" returns a string not a single character.
This code below seems to do what you wanted:
toStr :: (Num a, Show a) => [a] -> String
toStr (x:xs)
| null xs = show x -- avoid using length because of cost
| not (null xs) = (show x) ++ (toStr xs)
toStr [] = ""
main = do
let ls1 = [ 13, 17, 19, 23 ]
let st1 = toStr ls1
let st2 = concatMap show ls1 -- as per melpomene's remark
putStrLn $ "st1 = " ++ st1
putStrLn $ "st2 = " ++ st2
As a side remark, Haskell programmers generally avoid to use the length function when they just want to know whether a list is empty or not. Things can get really ugly if you do that, because of our potentially unlimited lazy lists.
I need to extract from a list elements in the odd positions. In the Data.List library I found anything about. So I created the following functions. I would like to know if there is a library that contains this functions and other similar and if it is possible to refactor my functions significantly. Thanks.
extractByPattern p l = extractByPatternRaw bp l
where
bp = map (== 't') p
extractByPatternRaw p l = foldr select [] coupledList
where
coupledList = zip (concat . repeat $ p) l
select (b,x) acc
| b = x : acc
| otherwise = acc
oddPos = extractByPattern "tf"
-- ex. oddPos [1..20] == [1,3,5,7,9,11,13,15,17,19]
everyTwoAndFivePos = extractByPattern "ftfft"
-- ex. everyTwoAndFivePos [1..20] == [2,5,7,10,12,15,17,20]
As an alternative:
λ map fst $ filter snd $ zip [1..20] $ cycle . map (== 't') $ "ftfft"
[2,5,7,10,12,15,17,20]
So you could do something like the following:
extractByPattern pattern list = map fst $ filter snd $ zip list $ cycle . map (== 't') $ pattern
Nothing jumps out in Hoogle for [Bool] -> [a] -> [a] or [a] -> [Bool] -> [a], which would save the zip-filter-snd-map-fst hoop-jumping.
I want make a function which when given a string eg "ab" and "cdabd" when it will be used on these two strings it will output "cd"
I have this up to now
takeUntil :: String -> String -> String
takeUntil [] [] = []
takeUntil xs [] = []
takeUntil [] ys = []
takeUntil xs ys = if contains xs ys then -- ???? I get stuck here.
the contains function is one in which I had defined previously(this whole function should be case insensitive)
contains function:
contains :: String -> String -> Bool
contains _ [] = True
contains [] _ = False
contains xs ys = isPrefixOf (map toLower ys) (map toLower xs) || contains (tail(map toLower xs) (map toLower ys)
There is a lot of ways to do this, but continuing with your path, try the following:
import Data.List
takeUntil :: String -> String -> String
takeUntil [] [] = [] --don't need this
takeUntil xs [] = []
takeUntil [] ys = []
takeUntil xs (y:ys) = if isPrefixOf xs (y:ys)
then []
else y:(takeUntil xs (tail (y:ys)))
Some outputs:
takeUntil "ab" "cdabd"
"cd"
takeUntil "b" "cdabd"
"cda"
takeUntil "d" "cdabd"
"c"
takeUntil "c" "cdabd"
""
takeUntil "xxx" "cdabd"
"cdabd"
EDIT:
The OP wants the function to be case-insensitive.
Well, again you can do that in lot of ways. For example you can write a lowerCase function like (i think you already have it in Data.Text):
import qualified Data.Char as Char
lowerCase :: String -> String
lowerCase [] = []
lowerCase (x:xs) = (Char.toLower x):(lowerCase xs)
And then use it like (maybe ugly and not very practical):
takeUntil (lowerCase "cd") (lowerCase "abcDe")
"ab"
That is the result you expect.
Also, you can use that lowerCase function inside takeUntil:
-- ...
takeUntil xs (y:ys) = if isPrefixOf (lowerCase xs) (lowerCase (y:ys))
-- ...
So, you can just do:
takeUntil "cd" "abcDe"
"ab"
Anyway, i think the best option is that one #bheklilr suggested. Make your own isPrefixOfCaseless function.
I hope this helps.
Within the numerous approaches to define takeUntil, consider using Data.Text functions as follows,
takeUntil :: String -> String -> String
takeUntil sep txt = unpack $ fst $ breakOn (pack sep) (toCaseFold $ pack txt)
Note that pack converts a String to a Text, whereas uncpak does the inverse; toCaseFold for case insensitive operations; breakOn delivers a pair where the first element includes text until the first (possible) match.
Update
This approach covers tests already suggested, yet it does not preseve original String for instance here,
takeUntil "e" "abcDe"
"abcd"
A work around this involves for instance the splitting by index at the break point.
I'm new in Haskell and I'm tring to write a simple function that counts the number of occurences of a substring in a string.
For example : "There is an apple" and I want to count how many times "is" in the sentence, in this case the result should be 1.
This is what I've tried:
countOf :: String -> Int
countOf x = length [n | n <- words x, filter "is" x]
According what I've studied it should work, but it doesn't. I really don't know how to solve the problem, and also don't know what the error message I get means:
input:1:41:
Couldn't match expected type `Bool' with actual type `[a0]'
In the return type of a call of `filter'
In the expression: filter "a" x
In a stmt of a list comprehension: filter "a" x
The function filter has the type
filter :: (a -> Bool) -> [a] -> [a]
This means that its first argument is another function, which takes an element and returns a Bool, and it applies this function to each element of the second argument. You're giving a String as the first argument instead of a function. Maybe you want something more like
countOf x = length [n | n <- words x, filter (\w -> w == "is") x]
But this won't work either! This is because any extra expression in a list comprehension has to be a Bool, not a list. filter returns a list of elements, not a Bool, and this is actually the source of your compiler error, it expects a Bool but it sees a list of type [a0] (it hasn't even gotten far enough to realize it should be [String]).
Instead, you could do
countOf x = length [n | n <- words x, n == "is"]
And this would be equivalent to
countOf x = length (filter (\w -> w == "is") (words x))
Or with $:
countOf x = length $ filter (\w -> w == "is") $ words x
Haskell will actually let us simplify this even further to
countOf x = length $ filter (== "is") $ words x
Which uses what is known as an operator section. You can then make it completely point free as
countOf = length . filter (== "is") . words
I would do like this:
countOf :: String -> Int
countOf x = length [n | n <- words x, compare "is" n == EQ]
Demo in ghci:
ghci> countOf "There is an apple"
1
You can put the comparison straight in the comprehension:
countOf x = length [n | n <- words x, n == "is"]
Actually, you try to count the number of occurences of a word in a string. In case you look for a substring:
import Data.List (inits, tails)
countOf = length . filter (=="is") . conSubsequences
where
conSubsequences = concatMap inits . tails
One could also try a foldr:
countOf :: String -> Int
countOf x = foldr count 0 (words x)
where
count x acc = if x == "is" then acc + 1 else acc
I've seen the other threads about missing split function but I didn't want to peek as I do this for learning purposes and want to figure out myself. So here it is:
split :: Char -> String -> [String]
split c xs | null f = []
| otherwise = f : split c s'
where (f,s) = break (== c) xs
s' | null s = s
| otherwise = tail s
It seems to work fine (please tell me if anything is wrong with it) but when I use a splitting character that is not in the string then the function returns a list with a single element of the original string whereas I want it to return an empty list. I can't figure out how to do it.
Any ideas?.
You can simply write a wrapper function, changing the result of your original one:
split' x xs = go (split x xs) where
go [_] = []
go ys = ys
There are many ways to write a split function, e.g.
split x xs = foldl' go [[]] xs where
go (ys:yss) y | y == x = []:ys:yss
| otherwise = (y:ys):yss
or
import Data.List
split x xs = map tail $ groupBy (const (/=x)) (x:xs)