I have a list [String] the task ist to remove those elements in the list, which have "q" or "p" and then capitalize all letters in the list with toUpper.
What I tried yet is as follow:
delAndUpper :: [String] -> [String]
delAndUpper myList = filter (\x -> not('p' `elem` x || 'q' `elem` x)) myList
It removes the unwanted elements from the list properly, however I can't apply toUpper on this list since the type of toUpper is Char.
I tried it with map and it does not work.
delAndUpper myList = map toUpper (filter (\x -> not('p' `elem` x || 'q' `elem` x)) myList)
I know, that toUpper in this line of code gets a list as value and therefore it can't work, but know how to go a level down into the list and the apply map toUpper.
Could you please help me.
Thanks in advance!
Greetings
Mapping one level deeper
You need to use map (map toUpper).
This is because you have [String] instead of String.
toUpper :: Char -> Char
map toUpper :: [Char] -> [Char]
i.e.
map toUpper :: String -> String
map (map toUpper) :: [String] -> [String]
map toUpper capitalises a String, by making each letter uppercase, so map (map toUpper) capitalises each String in a list of Strings.
Your function becomes
delAndUpper myList = map (map toUpper) (filter (\x -> not('p' `elem` x || 'q' `elem` x)) myList)
dave4420 made a good suggestion that (map.map) toUpper is a neat way of writing map (map toUpper) that helps you think two list levels in quite simply and naturally - have a look at his answer too.
Can we un-hardwire the p and q?
You asked if there was a shorter way to write the condition, and didn't like hard coding the `q` and `p`. I agree those multiple `elem` bits aren't pretty. Let's pass in the list of disallowed letters and tidy up a bit:
delAndUpper omit strings = map (map toUpper) (filter ok strings) where
ok xs = not (any (`elem` omit) xs)
Here (`elem` omit) checks a character if it's in the list of ones that would cause us to omit the word, so (any (`elem` omit) xs) checks if any of the characters of xs are forbidden. Of course if none are forbidden, it's ok.
Your original delAndUpper would now be delAndUpper "pq", or if you also want to disallow capital P and Q, delAndUpper "pqPQ". (More on this later.)
Can we make it more concise?
Let's see if we can't write ok a little shorter. My first thought was to use pointfree on it (see my answer to another question for details of how to get it running in ghci), but it seemed to hang, so using some standard transformation tricks, we can compose not with a function that takes two arguments before giving us a Bool by doing (not.).f instead of not.f as we would with a function which just gave us a Bool after the first input. any is taking (`elem` omit) as its first argument. This gives us
ok xs = ((not.).any) (`elem` omit) xs
from which we can remove the trailing xs:
ok = ((not.).any) (`elem` omit)
and inline:
delAndUpper omit strings = map (map toUpper) (filter (((not.).any) (`elem` omit)) strings)
I'm not keen on the trailing strings either:
delAndUpper omit = map (map toUpper).filter (((not.).any) (`elem` omit))
(We could get rid of the omit argument as well and go completely point free, but that would go quite a bit too far down the hard-to-read road for my taste.)
Whither Q?
> delAndUpper "pq" $ words "The Queen has probably never had a parking ticket."
["THE","QUEEN","HAS","NEVER","HAD","A","TICKET."]
Is this the required behaviour? It seems strange to carefully exclude the lowercase variants and then make everything uppercase. We could do it the other way round:
upperAndDel omit = filter (((not.).any) (`elem` omit)).map (map toUpper)
giving
> upperAndDel "PQ" $ words "The Queen has probably never had a parking ticket."
["THE","HAS","NEVER","HAD","A","TICKET."]
I know, that toUpper in this line of code gets a list as value and therefore it can't work, but know how to go a level down into the list and the apply map toUpper.
Use (map . map) instead of map.
n.b. (map . map) toUpper is the same as map (map toUpper) as suggested by the other answers. I mention it because, personally, I find it clearer: it looks more like it is going down two levels to apply toUpper. (You may not be familiar with the function composition operator (.), look it up or ask about it if you need to.)
Other functions with a similar type ((a -> b) -> something a -> something b), such as fmap, Data.Map.map, first and second, can be combined with each other and with map in a similar way.
Perhaps you don't find (map . map) toUpper clearer than map (map toUpper); fair enough.
You're almost there, you just need a second map.
map (map toUpper) (filter (\x -> not('p' `elem` x || 'q' `elem` x)) myList)
This is because String is completely synonymous with [Char] in vanilla haskell. Since the type of map is
(a->b) -> [a] -> b
and we have
toUpper :: Char -> Char
String :: [Char]
We'll get back another String, except capitalized.
By the way, that ugly-ish filter can be replaced made prettier with by making it use arrows :) (Think of these like more structured functions)
map (map toUpper) . filter $ elem 'p' &&& elem 'q' >>> arr (not . uncurry (||))
Gratuitousness? Maybe, but kinda cool.
Related
I have to simplify custom regex expressions parsed to a certain data type. With "simplify" I mean the following (emphasis mine):
Given the rules:
lowercase letters match themselves, eg.:
a matches a and nothing else
parens enclosing only letters match their full sequence, eg.:
(abc) matches abc and nothing else
square brackets enclosing only letters match every letters inside, eg.:
[abc] matches a and b and c and nothing else
The following are all valid:
(a[bc]) matches ab and ac and nothing else
[a(bc)] matches a and bc and nothing else
(a(bc)) is the same as (abc) and matches abc and nothing else
[a[bc]] is the same as [abc] and matches a and b and c and nothing else
Regexes can be simplified. For example [a[[bb]b[[b]]](c)(d)] is
really just the same as [abcd] which matches a, b, c and d.
I have implemented a simple parser combinator in Haskell using attoparsec and the following destination data type:
data Regex
= Symbol Char
| Concat [Regex] -- ()
| Union [Regex] -- []
deriving (Eq)
However, I'm really struggling with the simplification part. I try to reduce the Concats and Unions by a combination of unwrapping them, nubbing and concatMapping to no avail. I think that the data type I have defined might not be the best fit but I have run out of ideas (late at night here). Could you help me look to the right direction? Thanks!
simplify :: Regex -> Regex
simplify (Symbol s) = Symbol s
simplify (Concat [Symbol c]) = Symbol c
simplify (Concat rs) = Concat $ simplify <$> rs
simplify (Union [Symbol c]) = Symbol c
simplify (Union rs) = Union $ nub $ simplify <$> rs
You are missing a couple simple improvements, for starters. simplify (Concat [x]) = x and likewise for Union: there's no need for the wrapped regex to be specifically a symbol.
Then you need to start looking at Concats containing other Concats, and likewise for Union. Sure, you start by simplifying the elements of the wrapped list, but before jamming the result back into a wrapper, you lift up any elements using the same wrapper. Something like:
simplify (Concat xs) =
case concatMap liftConcats (map simplify xs) of
[x] -> x
xs -> Concat xs
where liftConcats :: Regex -> [Regex]
liftConcats r = _exerciseForTheReader
Then you can do something similar for Union, with a nub thrown in as well.
I was wondering what would be a good strategy to understand if pattern-matching in SML will proceed the Match warning.
Consider the following function:
fun f 7 (x,y) = x * 5.1 | f x (y,#"a") = y;
From first glance, it looks like it does not provide the Match warning. But if I'll run it, it will.
From my point of view, we handle all of the cases. which case we don't handle? even if f 7 (x,#"a") we know which case should be (first one).
My question is, how to decide that the function will output that waning.
Also, I would be glad for an answer why the following function is invalid:
fun f (x::xs) (y::ys) (z::zs) = y::xs::ys::zs;
without zs its valid. how does zs change it?
My question is, how to decide that the function will output that waning.
The compiler has an algorithm that decides this.
Either use the compiler and have it warn you, or use a similar heuristic in your head.
See Warnings for pattern matching by Luc Maranget (2007).
It covers the problem, algorithm and implementation of finding missing and duplicate patterns.
A useful heuristic: Line patterns up, e.g. like:
fun fact 0 = 1
| fact n = n * fact (n - 1)
and ask yourself: Is there any combination of values that is not addressed by exactly one case of the function? Each function case should address some specific, logical category of the input. Since your example isn't a practical example, this approach cannot be used, since there are no logical categories over the input.
And fact is a bit simple, since it's very easy to decide if it belongs to the categories 0 or n.
And yet, is the value ~1 correctly placed in one of these categories?
Here is a practical example of a function with problematic patterns:
fun hammingDistance [] [] = SOME 0
| hammingDistance (x::xs) (y::ys) =
if length xs <> length ys then NONE else
if x = y
then hammingDistance xs ys
else Option.map (fn d => d + 1) (hammingDistance xs ys)
It may seem that there are two logical cases: Either the lists are empty, or they're not:
The input lists are empty, in which case the first body is activated.
The input lists are not empty, in which case they have different or equal length.
If they have different lengths, NONE.
If they have equal lengths, compute the distance.
There's a subtle bug, of course, because the first list can be empty while the second one isn't, and the second list can be empty while the first one isn't. And if this is the case, the second body is never hit, and the distinction between different / equal lengths is never made. Because the task of categorizing is split between pattern matching and if-then-else with precedence to pattern matching.
What I do personally to catch problems like these preemptively is to think like this:
When I'm pattern matching on a list (just for example), I have to cover two constructors (1. [], 2. ::), and when I'm pattern matching on two lists, I have to cover the Cartesian product of its constructors (1. [], [], 2. [], ::, 3. ::, [], and 4. ::, ::).
I can count only two patterns/bodies, and none of them aim to cover more than one of my four cases, so I know that I'm missing some.
If there had been a case with variables, I have to ask how many of my common cases it covers, e.g.
fun hammingDistance (x::xs) (y::ys) =
if x = y
then hammingDistance xs ys
else Option.map (fn d => d + 1) (hammingDistance xs ys)
| hammingDistance [] [] = SOME 0
| hammingDistance _xs _ys = NONE
Here there's only three patterns/bodies, but the last one is a catch-all; _xs and _ys match all possible lists, empty or non-empty, except if they're matched by one of the previous patterns first. So this third case accounts for both of 2. [], :: and 3. ::, [].
So I can't simply count each pattern/body once. Some may account for more than one class of input if they contain very general patterns via pattern variables. And some may account for less of the total input space if they contain overly specific patterns via multiple constructors. E.g.
fun pairs (x::y::rest) = (x, y) :: pairs rest
| pairs [] = []
Here x::y::rest is so specific that I'm not covering the case of exactly one element.
I wrote the following code to find the last element of a list in haskell:
myLast (x:xs) = do
ret <- if xs == [] then x else (myLast xs)
return ret
The idea is to traverse the list until we are at an element which has the empty list as its next element. When we find it we set ret to that element.
It makes sense for me but when I run the code inside the interactive shell I get the following error:
<interactive>:1:1: error:
• No instance for (Num (m0 b0)) arising from a use of ‘it’
• In a stmt of an interactive GHCi command: print it
edit 1
The reason I used do was because I saw that pattern being used somewhere to also traverse a list, so I thought I could do the same here. I'am avoiding libraries for now to get comfortable with the language.
I wrote the function avoiding the do keyword and now it works:
myLast(x:xs) = if xs == [] then x else (myLast xs)
There's now just an issue with the empty list case. How to approach this in haskell?
let's start with the signature of your function
myLast :: [a] -> a
now, for an empty list input, what can be expected as the output? How you can make up an instance of an arbitrary type a?
Alternatively, you can defer the handling of missing last element to the callers of your function.
myLast :: [a] -> Maybe a
You want
myLast (x:xs) =
to be equal to
if xs == [] then x else (myLast xs)
Great, xs == [], so let's just put it back in:
myLast (x:[]) = x
but what about the else part? Well, let's add another equation for that,
myLast (_:xs) = myLast xs
and we're golden.
What if we call it with an empty list [] though? No definition case will match, and we will get some kind of a run-time error. Well, same thing happens with the built-in function last too, so we're no better and no worse than Haskell itself here.
What is that match that I mentioned, you ask? That's how Haskell functions get invoked. Each function definition can have several clauses, starting with the function's name, and containing a pattern for each expected argument.
In a left hand side of an equation,
(x:[]) is a pattern, matching any singleton list. It can also be written [x]. x will refer to the list's only element, if used in the right-hand side of the equation.
[] is a pattern, matching any empty list.
(x:xs) is a pattern, matching any non-empty list. x will refer to the list's head (i.e. first) element, if used in the right-hand side of the equation; and xs will refer to the rest of the elements in a list (which are also, a list -- also known as its tail).
But wait, you ask. Wouldn't both clauses match for a singleton list, the first for the pattern [x] and the second for (_:xs) with xs matched up with an empty list, []?
Why yes, they both would match indeed; (x:[]) and (_:xs) are not mutually exclusive.
But that's OK, because in Haskell, if the first clause has matched, that's it -- that is the clause that gets executed, and no other attempts at any additional pattern matching and clause selection are made.
That would be Prolog, and that's quite another language.
For a school exercise i need to generate a series of symbols with a given array of numbers. given is [3,3,2,1] output "+===+===+==+=+".
My approach would be to use map and replicate "=" on the array then intercalate "+" and finally concat the array to a single string.
My solution is something like this (while standing knee deep in errors)
printLine arr = map (replicate "=") arr >>> intercalate '*' >>> concat
what is the correct syntax? or shouldn't i use map at all?
you are on the right track, you just mixed up the functions a bit:
replicate will take a number n and repeat the second argument n-times into a list (so you just got the order wrong - you could use flip or an aux. function like I did bellow)
you have to watch out if you want Char or String ('=' VS "=" for example) - read the type-definitions (try :t intercalate or Hoogle) carefully and remember: String ~ [Char]!
intercalate actually does the concatenation so you don't need concat at all
Here is a almost working version:
eqSigns :: Int -> String
eqSigns n = replicate n '='
mixIn :: [Int] -> String
mixIn = intercalate "+" . map eqSigns
try it and see if you get the missing parts in there ;)
here is the version with flip instead:
mixIn :: [Int] -> String
mixIn = intercalate "+" . map (flip replicate '=')
PS: are you coming from some ML/F# background?
I've been trying to solve this pair tuples problem where the input is a list of tuples and the output is a tuple of lists where the first element of each tuple is grouped together and similarly with the second (i.e. [(1,2),(3,4),(5,6)] --> ([1,3,5],[2,4,6])).
I've thought of this code but it gives me an error:
fun convert L = foldl (fn ((x,y),(u,v)) => ((u#x),(v#y)) ([],[]) L;
Any suggestions for a fix?
Concatenation (#) takes two lists, but x and y are values, so you need to wrap them with [] to make a single-element list:
fun convert l=foldl (fn((x,y),(u,v))=>(u#[x],v#[y])) (nil,nil) l
You can use cons instead of concatenation, though the lists inside the returned tuple are reversed:
fun convert l=foldl (fn((x,y),(u,v))=>(x::u,y::v)) (nil,nil) l
# concatenates lists (and x and y are not lists).
Try (u#[x],v#[y]).
Note, however, that appending is a linear-time operation, while prepending (i.e. x::u) is constant. As Alex pointed out, this will build your lists in reverse, but you can resolve this by processing your input in reverse as well - i.e., by using foldr instead of foldl.