Haskell - Removing non-letter characters but ignoring white spaces? - haskell

I am very new to Haskell. I am trying to return a list of strings from a given string (which could contain non-letter characters) but I get a single string in the list.
The below code shows What I have tried so far:
toLowerStr xs = map toLower xs
--drop non-letters characters
dropNonLetters xs = words $ (filter (\x -> x `elem` ['a'..'z'])) $ toLowerStr xs
lowercase all the characters by using toLower function
remove non-letter characters by using filter function
return a list of strings by using words function
I think the filter function is removing the white spaces and therefore it becomes a single string. I tried using isSpace function but I don't know exactly how to implement it in this case.
What is it that I am doing wrong? I get this output:
λ> dropNonLetters "ORANGE, apple! APPLE!!"
["orangeappleapple"]
But I want to achieve the below output:
λ> dropNonLetters "ORANGE, apple! APPLE!!"
["orange","apple","apple"]

I think the filter function is removing the white spaces and therefore it becomes a single string.
That is correct. As filter predicate you write \x -> x `elem` ['a'..'z']. ['a'..'z'] is a list that contains lowercase letters, so for whitespace, the predicate will fail, and thus you should allow spaces as well.
We can for instance add the space character to the list:
dropNonLetters xs = words $ (filter (\x -> x `elem` (' ':['a'..'z'])))) $ toLowerStr xs
But this is inelegant and does not really explain itself. The Data.Char module however ships with two functions that are interesting here: isLower :: Char -> Bool, and isSpace :: Char -> Bool. We can use this like:
dropNonLetters xs = words $ (filter (\x -> isLower x || isSpace x)) $ toLowerStr xs
isLower and isSpace are not only more "descriptive" and elegant. Usually these functions will be faster than a membership check (which will usually be done in O(n)), and furthermore it will also take into account tabs, new lines, etc.
We can also perform an eta-reduction on the function:
dropNonLetters = words . (filter (\x -> isLower x || isSpace x)) . toLowerStr
This then produces:
Prelude Data.Char> dropNonLetters "ORANGE, apple! APPLE!!"
["orange","apple","apple"]
I advise you to rename the function dropNonLetters, since now it does not fully explain that it will generate a list of words. Based on the name, I would think that it only drops non-letters, not that it converts the string to lowercase nor that it constructs words.

here's an example of separating characters into separate string lists:
sortNumbers :: [Char] -> [String]
sortNumbers args = filter (\strings ->strings/= "") $ zipWith (\x numbers -> filter (\char -> char == numbers) x) (repeat args)
['1'..'9']

Related

How to write a password strength testing program in Haskell?

I am very new to Haskell, and I found some exercises on the internet, and one of them asks to write a program that takes a String and checks if the word has at least 8 characters, 1 uppercase letter, and 1 digit. In case it does, it returns true.
However I have absolutely no idea what to do.
I have tried to work with a Lambda expressions but I think it's not the way to go.
Would appreciate some help,
Thanks
I would suggest something like this.
import Data.Char
checkPassword :: String -> Bool
checkPassword xs = all (\f -> f xs) [checkOneDigit, checkOneUppercase, checkLength]
where checkOneDigit xs = (== 1) $ length $ filter isDigit xs
checkOneUppercase xs = (== 1) $ length $ filter isUpper xs
checkLength xs = (>= 8) $ length xs

Haskell - Exclude lists based on a test in a nested list comprehension

I want to create a series of possible equations based on a general specification:
test = ["12", "34=", "56=", "78"]
Each string (e.g. "12") represents a possible character at that location, in this case '1' or '2'.)
So possible equations from test would be "13=7" or "1=68".
I know the examples I give are not balanced but that's because I'm deliberately giving a simplified short string.
(I also know that I could use 'sequence' to search all possibilities but I want to be more intelligent so I need a different approach explained below.)
What I want is to try fixing each of the equals in turn and then removing all other equals in the equation. So I want:
[["12","=","56","78"],["12","34","=","78”]]
I've written this nested list comprehension:
(it needs: {-# LANGUAGE ParallelListComp #-} )
fixEquals :: [String] -> [[String]]
fixEquals re
= [
[
if index == outerIndex then equals else remain
| equals <- map (filter (== '=')) re
| remain <- map (filter (/= '=')) re
| index <- [1..]
]
| outerIndex <- [1..length re]
]
This produces:
[["","34","56","78"],["12","=","56","78"],["12","34","=","78"],["12","34","56","”]]
but I want to filter out any with empty lists within them. i.e. in this case, the first and last.
I can do:
countOfEmpty :: (Eq a) => [[a]] -> Int
countOfEmpty = length . filter (== [])
fixEqualsFiltered :: [String] -> [[String]]
fixEqualsFiltered re = filter (\x -> countOfEmpty x == 0) (fixEquals re)
so that "fixEqualsFiltered test" gives:
[["12","=","56","78"],["12","34","=","78”]]
which is what I want but it doesn’t seem elegant.
I can’t help thinking there’s another way to filter these out.
After all, it’s whenever "equals" is used in the if statement and is empty that we want to drop the equals so it seems a waste to build the list (e.g. ["","34","56","78”] and then ditch it.)
Any thoughts appreciated.
I don't know if this is any cleaner than your code, but it might be a bit more clear and maybe more efficient using a recursion:
fixEquals = init . f
f :: [String] -> [[String]]
f [] = [[]]
f (x:xs) | '=' `elem` x = ("=":removeEq xs) : map (removeEq [x] ++) (f xs)
| otherwise = map (x:) (f xs)
removeEq :: [String] -> [String]
removeEq = map (filter (/= '='))
The way it works is that, if there's an '=' in the current string, then it splits the return into two, if not just calls recursively. The init is needed as in the last element returned there's no equal in any string.
Finally, I believe you can probably find a better data structure to do what you need to achieve instead of using list of strings
Let
xs = [["","34","56","78"],["12","=","56","78"],["12","34","=","78"],["12","34","56",""]]
in
filter (not . any null) xs
will give
[["12","=","56","78"],["12","34","=","78"]]
If you want list comprehension then do
[x | x <- xs, and [not $ null y | y <- x]]
I think I'd probably do it this way. First, a preliminary that I've written so many times it's practically burned into my fingers by now:
zippers :: [a] -> [([a], a, [a])]
zippers = go [] where
go _ [] = []
go b (h:e) = (b,h,e):go (h:b) e
Probably running it once or twice in ghci will be a more clear explanation of what this does than any English writing I could do:
> zippers "abcd"
[("",'a',"bcd"),("a",'b',"cd"),("ba",'c',"d"),("cba",'d',"")]
In other words, it gives a way of selecting each element of a list in turn, giving the "leftovers" of what was before and after the selection point. Given that tool, here's our plan: we'll nondeterministically choose a String to serve as our equals sign, double-check that we've got an equals sign in the first place, and then clear out the equals from the others. So:
fixEquals ss = do
(prefix, s, suffix) <- zippers ss
guard ('=' `elem` s)
return (reverse (deleteEquals prefix) ++ ["="] ++ deleteEquals suffix)
deleteEquals = map (filter ('='/=))
Let's try it:
> fixEquals ["12", "34=", "56=", "78"]
[["12","=","56","78"],["12","34","=","78"]]
Perfect! But this is just a stepping-stone to actually generating the equations, right? It turns out to be not that hard to go all the way in one step, skipping this intermediate. Let's do that:
equations ss = do
(prefixes, s, suffixes) <- zippers ss
guard ('=' `elem` s)
prefix <- mapM (filter ('='/=)) (reverse prefixes)
suffix <- mapM (filter ('='/=)) suffixes
return (prefix ++ "=" ++ suffix)
And we can try it in ghci:
> equations ["12", "34=", "56=", "78"]
["1=57","1=58","1=67","1=68","2=57","2=58","2=67","2=68","13=7","13=8","14=7","14=8","23=7","23=8","24=7","24=8"]
The easiest waty to achieve what you want is to create all the combinations and to filter the ones that have a meaning:
Prelude> test = ["12", "34=", "56=", "78"]
Prelude> sequence test
["1357","1358","1367","1368","13=7","13=8","1457","1458","1467","1468","14=7","14=8","1=57","1=58","1=67","1=68","1==7","1==8","2357","2358","2367","2368","23=7","23=8","2457","2458","2467","2468","24=7","24=8"
Prelude> filter ((1==).length.filter('='==)) $ sequence test
["13=7","13=8","14=7","14=8","1=57","1=58","1=67","1=68","23=7","23=8","24=7","24=8","2=57","2=58","2=67","2=68"]
You pointed the drawback: imagine we have the followig list of strings: ["=", "=", "0123456789", "0123456789"]. We will generate 100 combinations and drop them all.
You can look at the combinations as a tree. For the ["12", "34"], you have:
/ \
1 2
/ \ / \
3 4 3 4
You can prune the tree: just ignore the subtrees when you have two = on the path.
Let's try to do it. First, a simple combinations function:
Prelude> :set +m
Prelude> let combinations :: [String] -> [String]
Prelude| combinations [] = [""]
Prelude| combinations (cs:ts) = [c:t | c<-cs, t<-combinations ts]
Prelude|
Prelude> combinations test
["1357","1358","1367","1368","13=7","13=8","1457","1458","1467","1468","14=7","14=8","1=57","1=58","1=67","1=68","1==7","1==8","2357","2358","2367","2368","23=7","23=8","2457","2458","2467","2468","24=7","24=8", ...]
Second, we need a variable to store the current number of = signs met:
if we find a second = sign, just drop the subtree
if we reach the end of a combination with no =, drop the combination
That is:
Prelude> let combinations' :: [String] -> Int -> [String]
Prelude| combinations' [] n= if n==1 then [""] else []
Prelude| combinations' (cs:ts) n = [c:t | c<-cs, let p = n+(fromEnum $ c=='='), p <= 1, t<-combinations' ts p]
Prelude|
Prelude> combinations' test 0
["13=7","13=8","14=7","14=8","1=57","1=58","1=67","1=68","23=7","23=8","24=7","24=8","2=57","2=58","2=67","2=68"]
We use p as the new number of = sign on the path: if p>1, drop the subtree.
If n is zero, we don't have any = sign in the path, drop the combination.
You may use the variable n to store more information, eg type of the last char (to avoid +* sequences).

How can split a string with two conditions?

So basically I want to split my string with two conditions , when have a empty space or a diferent letter from the next one.
An example:
if I have this string ,"AAA ADDD DD", I want to split to this, ["AAA","A","DDD","DD"]
So I made this code:
sliceIt :: String -> [String]
sliceIt xs = words xs
But it only splits the inicial string when an empty space exists.
How can I also split when a caracter is next to a diferent one?
Can this problem be solve easier with recursion?
So you want to split by words and then group equal elements in each split. You have the functions for doing so,
import Data.List
sliceIt :: String -> [String]
sliceIt s = concatMap group $ words s
sliceItPointFree = concatMap group . words -- Point free notation. Same but cooler
split :: String -> [String]
split [] = []
split (' ':xs) = split xs
split (x:xs) = (takeWhile (== x) (x:xs)) : (split $ dropWhile (== x) (x:xs))
So this is a recursive definition where there are 2 cases:
If head is a space then ignore it.
Otherwise, take as many of the same characters as you can, then call the function on the remaining part of the string.

Haskell filter more than one char

I have a question. I have this line of code here.
map length [filter (/= ' ') someString]
I know it removes the space from someString. But is it possible to remove more than just the space from the string using filter? Let just say remove the spaces and some other char.
Thanks!
You can just do:
filter (not . flip elem "<your chars here>")
Example:
ghci> filter (not . flip elem " .,") "This is an example sentence, which uses punctuation."
"Thisisanexamplesentencewhichusespunctuation"
Just to put the comment in here: To filter out letters of both cases (a-z and A-Z), you should probably use Data.Char's isLetter function.
A slightly shorter way is to use notElem:
λ> filter (flip notElem " f") "foo bar"
"oobar"
An alternative is to use a fold to get the length, if you don't actually need to return the filtered string but only want its length.
f x charlist = foldl (\acc x -> if not $ x `elem` charlist then acc + 1 else acc) 0 x
charlist is your list of banned characters. If you have a fixed list always, you can directly define it as a top-level value and use it in the function body instead of passing it as a parameter.
A more concise version using list comprehensions:
f x = length [z | z <- x, notElem z charlist]

Haskell compare all list items

Edit: it's hard to describe what I'm trying to do, but here's a try (from the comments):
I am building a wordfeud solver, so I have a word, and some letters (both char list). I applied this ( How to find the frequency of characters in a string in Haskell? ) to both lists to get the frequency of all letters. What I'm doing now is iterating though the 'word' char list, and checking if all chars occur sufficiently in the 'letters' char list.
I have written a Haskell function that compares two lists by applying a function to the items of both lists, and comparing the results.
The comparison is done like this:
hasLetters' :: [Char] -> [Char] -> Bool
hasLetters' word letters = (getCharOccurrence (head word) (getCharOccurrences word)) <= (getCharOccurrence (head word) (getCharOccurrences letters))
This only compares the occurrences of the first letter of the word. But ALL words should be compared (and the result should be TRUE for all of them).
I don't really know how to accomplish this. I found the 'all' method that lets me define a predicate, that's pretty good. It looks like this:
all (<= (getCharOccurrence (head word) (getCharOccurrences letters)))
(I think that's correct)
It makes sure that every item that goes into the list is smaller than or equal to the result of the provided function.
BUT: the 'all' method needs another parameter. This would be the 'source' parameter that defines what should be compared to the predicate. This would be easy when this were just a list, then I would do something like this:
all (<= (getCharOccurrence (head word) (getCharOccurrences letters))) [0..10]
But the problem is: I dont have a list of results like this, I need to compare it to the result of:
(getCharOccurrence (head word) (getCharOccurrences letters))
I figured that I could apply this function to every character in the 'word' char list with the 'map' function, but I dont know how to use it. I started like this:
map (getCharOccurrence (head word) (getCharOccurrences word)) word
But that's wrong.
So what I (think I) need: apply the above function to all characters of the 'word' char list, and compare it to the predicate.
But maybe I'm just think the wrong way. I'm an absolute Haskell/functional programming newbie. Please help me out :-)
Using the multiset package:
import Data.MultiSet
compareAll as bs = fromList as `isSubsetOf` fromList bs
or:
import Data.Function
import Data.MultiSet
compareAll = isSubsetOf `on` fromList
So from what I understand you have a string word with the word you would like to form and a list of chars letters representing the tiles at your disposal. You want to check whether the word may be formed by the tiles or not.
Here I'm assuming the functions you've mentioned have the types
getCharOccurrence :: Char -> [(Char, Integer)] -> Integer
getCharOccurrences :: [Char] -> [(Char, Integer)]
First, you need to modify hasLetters' to take a Char parameter instead of using head word:
hasLetters' :: Char -> [Char] -> [Char] -> Bool
hasLetters' c word letters = (getCharOccurrence c (getCharOccurrences word)) <= (getCharOccurrence c (getCharOccurrences letters))
Then you can combine the above to a master function (let's call it sufficientTiles) with
sufficientTiles :: [Char] -> [Char] -> Bool
sufficientTiles word letters = and $ map (\c -> hasLetters' c word letters) word
What we've done here is to map the hasLetter' function to each character of word. This will give us a list of Bools. We then use and to check that all elements of that list are True.
So I think I understand that you want to compare two lists. The test passes if the second list has at least as many of each element as the first list. Thus, you need a constraint of at least equality but order would help.
There are many solutions, the one that comes to mind first is sort each list, group and count the unique elements, and ensure all results are <=:
someCompare a b = let op :: String -> [(Char,Int)]
op = map (head &&& length) . group . sort
in [c <= k | (x,c) <- op a, k <- maybeToList (lookup x (op b))]
Or you can use a map of counters and union the two maps:
someCompare2 a b =
let op = foldl' (\m c -> Map.insertWith (+) c 1 m) Map.empty
in all (<= 0) . Map.elems $ Map.unionWith (+) (op a) (Map.map negate $ op b)
Etc etc.

Resources