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?
Related
I have just started to learn ocaml and I find it difficult to extract small list of chars from a bigger list of chars.
lets say I have:
let list_of_chars = ['#' ; 'a' ; 'b' ; 'c'; ... ; '!' ; '3' ; '4' ; '5' ];;
I have the following knowledge - I know that in the
list above I have '#' followed by a '!' in some location further in the list .
I want to extract the lists ['a' ;'b' ;'c' ; ...] and ['3' ; '4' ; '5'] and do something with them,
so I do the following thing:
let variable = match list_of_chars with
| '#'::l1#['!']#l2 -> (*[code to do something with l1 and l2]*)
| _ -> raise Exception ;;
This code doesn't work for me, it's throwing errors. Is there a simple way of doing this?
(specifically for using match)
As another answer points out, you can’t use pattern matching for this because pattern matching only lets you use constructors and # is not a constructor.
Here is how you might solve your problem
let split ~equal ~on list =
let rec go acc = function
| [] -> None
| x::xs -> if equal x on then Some (rev acc, xs) else go (x::acc) xs
in
go [] list
let variable = match list_of_chars with
| '#'::rest ->
match split rest ~on:'!' ~equal:(Char.equal) with
| None -> raise Exception
| Some (left,right) ->
... (* your code here *)
I’m now going to hypothesise that you are trying to do some kind of parsing or lexing. I recommend that you do not do it with a list of chars. Indeed I think there is almost never a reason to have a list of chars in ocaml: a string is better for a string (a chat list has an overhead of 23x in memory usage) and while one might use chars as a kind of mnemonic enum in C, ocaml has actual enums (aka variant types or sum types) so those should usually be used instead. I guess you might end up with a chat list if you are doing something with a trie.
If you are interested in parsing or lexing, you may want to look into:
Ocamllex and ocamlyacc
Sedlex
Angstrom or another parser generator like it
One of the regular expression libraries (eg Re, Re2, Pcre (note Re and Re2 are mostly unrelated)
Using strings and functions like lsplit2
# is an operator, not a valid pattern. Patterns need to be static and can't match a varying number of elements in the middle of a list. But since you know the position of ! it doesn't need to be dynamic. You can accomplish it just using :::
let variable = match list_of_chars with
| '#'::a::b::c::'!'::l2 -> let l1 = [a;b;c] in ...
| _ -> raise Exception ;;
I'm quite new to Haskell and I'm trying to solve the following problem:
I have a function, that produces an infinite list of strings with different lengths. But the number of strings of a certain length is restricted.
Now I want to extract all substrings of the list with a certain length n . Unfortunately I did a lot of research and tried a lot of stuff, but nothing worked for me.
I know that filter() won't work, as it checks every part of the lists and results in an infinite loop.
This is my function that generates the infinite list:
allStrings = [ c : s | s <- "" : allStrings, c <- ['R', 'T', 'P']]
I've already tried this:
allStrings = [x | x <- [ c : s | s <- "" : allStrings,
c <- ['R', 'T', 'P']], length x == 4]
which didn't terminate.
Thanks for your help!
This
allStrings4 = takeWhile ((== 4) . length) .
dropWhile ((< 4) . length) $ allStrings
does the trick.
It works because your (first) allStrings definition cleverly generates all strings containing 'R', 'T', and 'P' letters in productive manner, in the non-decreasing length order.
Instead of trying to cram it all into one definition, separate your concerns! Build a solution to the more general problem first (this is your allStrings definition), then use it to solve the more restricted problem. This will often be much simpler, especially with the lazy evaluation of Haskell.
We just need to take care that our streams are always productive, never stuck.
The problem is that your filter makes it impossible to generate any solutions. In order to generate a string of length 4, you first will need to generate a string of length 3, since you each time prepend one character to it. In order to generate a list of length 3, it thus will need to generate strings of length 2, and so on, until the base case: an empty string.
It is not the filter itself that is the main problem, the problem is that you filter in such a way that emitting values is now impossible.
We can fix this by using a different list that will build strings, and filter that list like:
allStrings = filter ((==) 4 . length) vals
where vals = [x | x <- [ c : s | s <- "" : vals, c <- "RTP"]]
This will emit all lists of length 4, and then get stuck in an infinite loop, since filter will keep searching for more strings, and fail to find these.
We can however do better, for example by using replicateM :: Monad m => Int -> m a -> m [a] here:
Prelude Control.Monad> replicateM 4 "RTP"
["RRRR","RRRT","RRRP","RRTR","RRTT","RRTP","RRPR","RRPT","RRPP","RTRR","RTRT","RTRP","RTTR","RTTT","RTTP","RTPR","RTPT","RTPP","RPRR","RPRT","RPRP","RPTR","RPTT","RPTP","RPPR","RPPT","RPPP","TRRR","TRRT","TRRP","TRTR","TRTT","TRTP","TRPR","TRPT","TRPP","TTRR","TTRT","TTRP","TTTR","TTTT","TTTP","TTPR","TTPT","TTPP","TPRR","TPRT","TPRP","TPTR","TPTT","TPTP","TPPR","TPPT","TPPP","PRRR","PRRT","PRRP","PRTR","PRTT","PRTP","PRPR","PRPT","PRPP","PTRR","PTRT","PTRP","PTTR","PTTT","PTTP","PTPR","PTPT","PTPP","PPRR","PPRT","PPRP","PPTR","PPTT","PPTP","PPPR","PPPT","PPPP"]
Note that here the last character first changes when we generate the next string. I leave it as an exercise to obtain the reversed result.
I got a function that takes a string and search it for decimals and output them.
What I got so far is:
getDecimal :: String -> [Int]
getDecimal[] = 0
getDecimal (x:y:xs) =
if
isDecimal x y
then
//output list
else
getDecimal xs
(for example: getDecimal "he12llo035" will output 12 , 035
But cant fill in the then because I simply cant find the solution, can you guys give me tips?
You try to tackle this in an imperative-like way: "look at this part of the list, is it what we want? Well then do that thing... oh, no it's not? Well then proceed there..."
It sure can't hurt to know how to implement it in such terms as well, but anyway better to learn more concise, explanative, declarative approaches right away. "The Haskell way".
That normally means roughly: split up your goal in subtasks. You'll first want to group all numbers down to one element, rather than a group of characters within the list. Well, you can hoogle for "group"!
Indeed there's a group comparison, but it simply puts together elements that are equal. That's a bit too strong, we sure want to allow different digits in each number! groupBy is the version that allows this. So, according to what do we want to group? Well, depending on whether it's a number. There's a nice helper from the Data.Function module that allows you to formulate this very much to the point:
groupBy ((==) `on` isDigit)
Meaning, we want to group such elements that have "the same status of digit-being".
After that, you'll have a lists of strings, and know each string is either a complete number, or something unrelated. The latter is to be thrown away...
filter (isDigit . head)
(I've just checked if the first character is a digit; the following are sure to be as well because that's what we've grouped by.
Now you've a list of numbers still in string form, all that's left to be done is parse (read!) each of them.
import Data.Char
import Data.List
import Data.Function
getDecimalNums :: String -> [Int]
getDecimalNums = map read . filter (isDigit . head) . groupBy ((==) `on` isDigit)
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.
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.