I am trying to learn some Haskell and I find it difficult. I am having some issues with my
current project. The idea is that I have to go through a String and substitute certain chars
with new substrings. For instance if I have a String "FLXF" and I want to replace every F
with a substring called "FLF" the result should be "FLFLXFLF". Now I have been working on this
specific problem for hours. I have been reading up on types, different functions that might come in handy (map, fold, etc) and yet I have not been able to solve this problem.
The code below is some of the different tries I have had:
apply :: String -> String
apply [] = []
apply (x:xs) = if (x == 'F')
then do show "Hello"
apply xs
else (apply (xs))
This example here I was just trying to show hello every time I encountered a 'F', but all it shows is "", so this clearly does not work. I am really not sure an if else statement is the way to go here. I was also thinking the function map might do the trick. Here the code I was thinking about could look something like this:
map (\x y -> if y == 'F' then "FLD" else y) "FLF"
but that gives me a type error. So as you can see I am lost. Excuse me my poor knowledge to Haskell, but I am still new to it. I really hope some of you can help me out here or give me a push in the right direction. Feel free to ask questions if I have been unclear about something.
Thank you in advance!
John
map (\x y -> if y == 'F' then "FLD" else y) "FLF"
This is nearly right.
First... why does the function take two arguments?
map (\y -> if y == 'F' then "FLD" else y) "FLF"
The remaining type error is because the then branch gives a String, but the else branch gives a Char (the two branches must each give a value of the same type). So we'll make the else branch give a String instead (recall that String is a synonym for [Char]):
map (\y -> if y == 'F' then "FLD" else [y]) "FLF"
Now the problem is that this gives you a [String] value instead of a String. So we'll concatenate all those strings together:
concat (map (\y -> if y == 'F' then "FLD" else [y]) "FLF")
This combination of concat and map is common enough that there's a standard function that combines them.
concatMap (\y -> if y == 'F' then "FLD" else [y]) "FLF"
concatMap is the most intuitive thing here. This kind of combination between mapping over a data structure a function that does itself return the type of the data structure (in this case, a list) and combining the results back into a single "tight" list is indeed very common in Haskell, and indeed not only for lists.
I'd like to explain why your first attempt compiles at all, and what it actually does – because it's completely different from what you probably think!
apply (x:xs) = if (x == 'F')
that line is still perfectly clear: you just take the first char off the string and compare it to 'F'. At bit "pedestrian" to manually take the string apart, but fine. Well, the name you gave the function is not particularly great, but I'll stick with it here.
then do show "Hello"
now this is interesting. You probably think do starts a list of points, "first do this, then do that"... like in simple Hello, World-ish example programs. But always remember: in Haskell, there's normally no such thing as an order in which stuff is calculated. That only happens in the IO context. But there's no IO in your code!?!
Not sure if you've heard about what IO actually is, anyway here you go: it's a Monad. Those "mythical Haskell constructs you've only read about in story books"...
Indeed, though this might lead a bit far here, this question covers all there is to know about Monads! How is that?
Here's another (correct!) way do define your function.
apply' str = do
x <- str
if (x == 'F')
then "FLF"
else return x
So I'm using this weird do syntax, and it's not in IO, and it looks completely different from what you'd write in IO, but it works. How?
x <- str
In do notation, variable <- action always means something like "take one value out of this monadic thingy, and call it x". What you've probably seen is something like
response <- getLine
which means "take a user input out of the real world (out of the IO monad!) and call it response". In x <- str, it's a string that we have, not an IO action. So we take a character out of a string – nice and easy!
Actually, it's not quite right, though. "take a character" is what you do with apply (x:xs) = ..., which simply takes the first one. In contrast, x <- str actually takes all possible characters out of the string, one by one. If you're used to procedural languages, this may seem very inconsistent with response <- getLine, but in fact it's not: getLine also consists of every possible input that the user might give, and the program has to act according to this.
if (x == 'F')
nothing unexpected here, but
then "FLF"
whoah! Just like that? Let's first look at the next line
else return x
ok, this looks familiar, but actually it's not. In other languages, this would mean "we're done with our function, x is the result". But that's obviously not what happens here, because x is Char, and the "return type" of apply' is String. In Haskell, return actually has little to do with returning values from a function, instead it means "put that value into the monadic context that we're working in". If the monad were IO, that would be quite the same: give this value back to the real-world context (this does not mean to print the value or something, just to hand it on). But here, our context is a string, or rather a list (of chars, so it is a String).
Right, so if x is not 'F' we put it back into the string. That sounds reasonable enough, but what about then "FLF"? Note that I can also write it this way:
if (x == 'F')
then do
x' <- "FLF"
return x'
else return x
which means, I take all characters out of "FLW" and return them back into the overall result. But there's no need to only think about the final result, we can as well isolate only this part do { x' <- "FLF"; return x' } – and, quite obviously, its value is nothing but the string "FLF" itself!
So I hope you have now grasped why apply' works. Back to your version, though it actually doesn't make much sense...
then do
show "Hello"
apply xs
here we have a line that's not at the end of a do block, but doesn't have a <- in it. You normally see this in IO in something like
main = do
putStrLn "How ya doin'?"
response <- getLine
...
Remember that "output-only" actions have type IO() in Haskell, which means, they don't directly return any meaningful value, just the trivial value (). So you don't really care about this, but you could still evaluate it:
main = do
trivial <- putStrLn "Hello, let's see what this IO action returns:"
print trivial
compiles and outputs
Hello, let's see what this IO action returns:()
It would be stupid if we had to do this evaluating () all the time, so Haskell allows to just leave the () <- out. It's really just that!
So a line like show "Hello" in the middle of a do block basically means "take one character out of show "Hello" (which is simply a string with the value "\"Hello\""), but don't do anything else with this character / just throw it away".
The rest of your definition is just other recursive calls to apply, but because none of them does anything more interesting than throwing away characters, you eventually end up at apply [] = [], so that's the final result: an empty string.
if-then-else... I know that Haskell supports these, however, I'm very surprised that no one here removed them...
So below are my solutions for different cases of making replacements.
Replacing a character
Replacing words
Replacing through a function on each word
$ cat replace.hs
import Data.List (isPrefixOf)
replaceC :: Char -> Char -> String -> String
replaceC _ _ [] = []
replaceC a b (x:xs)
| x == a = b:replaceC a b xs
| otherwise = x:replaceC a b xs
replaceW :: String -> String -> String -> String
replaceW a b s = unwords . map replaceW' $ words s
where replaceW' x | x == a = b
| otherwise = x
replaceF :: (String -> String) -> String -> String
replaceF f = unwords . map f . words
string = "Hello world ^fg(blue)"
main = do
print string
print $ replaceC 'o' 'z' string
print $ replaceW "world" "kitty" string
print . replaceF f . replaceW "world" "kitty" $ replaceC 'H' 'Y' string
where f s | "^" `isPrefixOf` s = '^':'^':drop 1 s
| otherwise = s
$ runhaskell replace.hs
"Hello world ^fg(blue)"
"Hellz wzrld ^fg(blue)"
"Hello kitty ^fg(blue)"
"Yello kitty ^^fg(blue)"
Your basic error was that you wanted to replace a Char in a String with a String.
This is impossible because String is a list of Char and a Char is a Char and not a short String. Neither is a String ever a Char, even if its length is 1.
Hence, what you really wanted is to replace some Char with some other Chars. Your approach was promising and could have been completed like so:
replace [] = [] -- nothing to replace in an empty string
replace (c:cs) = if c == 'F' then 'F':'L':'F':replace cs
else c:replace cs
Related
I want to input two strings, "Hello" and "Hi" for example, and I want to go through every element in the two strings simultaneously and compare every character one by one. The two strings length in the actual program should be 5, if a char is equal to the other, return true, otherwise return false.
The program I have in mind should see 'H' in "Hello" and 'H' from "Hi" and return true, then I want it to check the 'e' in "Hello" and the 'i' in "Hi" and return false. I want it to keep on doing that till there is nothing to compare. I think I might need to use recursion but I am not sure how to implement it in this program really.
I tried using x:xs with the maximum range of 5 so [0..5], but it didn't work at all.
My code (not working):
uncurryingString :: String -> Int -> Char
uncurryingString a b = a !! b
match :: String -> String -> [Int] -> Bool
match a b (x:xs)
| uncurryingString a [x+1 | x <- xs] == uncurryingString b [x+1 | x <- xs] = True
| otherwise = False
You're thinking about this way too complicated.
First, as a rule of thumb, you should never use !! (whether directly or via some helper – uncurryingString is in fact exactly the same as !! itself). If direct indexing is required, a list is not the right data structure. But very often direct indexing is not required, it's just what's customarily used in some other programming languages that don't have pattern matching to do it more elegantly.
In your application, you're deconstructing both strings in parallel. Well, you should express that with a pattern match:
match (a:as) (b:bs) (x:xs) = ...
And now you can simply compare a and b directly, no need for messing about with any list comprehensions or indexing operators. In the example input "Hello" and "Hi", both a and b will be 'H' here.
But you probably don't want to return True right there, because there's still the rest of the string to be matched. That's where recursion comes in.
Finally, you need to clauses in case not all of the input lists are nonempty. Try to figure that out yourself.
What you probably don't need at all is the extra [Int] argument (x:xs). It could make sense to have a depth-limiting argument, but that could be simply an Int. You would then do something like
match :: Eq a => [a] -> [a] -> Int -> Bool
match _ _ 0 = True -- lists are equal up to the specified max depth
match (a:as) (b:bs) n = ...
...
Using a list comprehension is often a good way to compute things in Haskell, but not in this particular case.
I could try this code:
[a==b | a <- "Hello", b <- "Hi"]
What does this do? You might think that this returns True, because the 'H' letters match - or you might thing that this returns False, because the other letters don't match. In fact, it does both - in effect, it is running multiple nested loops.
[True,False,False,False,False,False,False,False,False,False]
So, the takeaway is to use list comprehensions when you want nested loops, or just working through one dataset. Here we just want a single loop that works through the two words in parallel. Hence it must be recursion.
I tried to implement a function that takes a limit and a string, parses the string and tests if the parsed number exceeds the limit. The function works well only for strings without 0s in it, like "123". However, it could not parse strings correctly like "100", whose result is 1.
What caused this problem?
Below is the code.
reachBounded :: Int -> String -> Maybe Int
reachBounded limit str = case str of
"" -> Nothing
"0" -> Just 0
_ -> foldr (\digit s -> do
sum <- s
let n = sum * 10 + digitToInt digit
guard (isDigit digit)
guard (n <= limit)
return n)
(Just 0) str
Moreover, is there any way to debug this code like we normally do in imperative languages? I found ghci debugger only able to print the type, not the value.
This is a very imperative way of solving the problem, and if you keep thinking like that you're going to have difficulties moving forward.
Here's how you might want to re-think the problem:
Replace "I have a list of characters, but I want digits, I'll iterate and replace them one by one" with "I have a list of characters but I want digits, I'll just replace them all at once" (I'm going to assume you want to actually parse the string yourself fully manually rather than just using read or some kind of parsing tool)
So far we have:
reachBounded limit str = ... map digitToInt str
Next, you want to turn these digits into a number. Replace "I want to iterate through this list increment a sum" with "I need to know the place value of each digit". We can do this by reversing the digits and multiplying them pairwise with the list [1,10,100,1000...]. We can produce the place value list by mapping (10^) over the list of positive integers, or declaring that each element is 10 times the previous, starting with 1. Let's use the latter:
reachBounded limit str = ... zipWith (*) (iterate (*10) 1) $ reverse $ map digitToInt str
And we want the sum of these place values:
reachBounded limit str = ... where
val = sum $ zipWith (*) (iterate (*10) 1) $ reverse $ map digitToInt str
Lastly, we must check if it's within the bound given:
reachBounded limit str = val <$ guard (val < limit) where
val = sum $ zipWith (*) (iterate (*10) 1) $ reverse $ map digitToInt str
In this case a <$ b will replace the contents of b with a if b is Just something, and leave it alone if b is Nothing.
In terms of debugging, it is now trivial, as it is not some process we need to interrupt, but a series of values that we manipulate to get the desired result. You cannot run part of your process on each step and get a sensible answer, but here we can look at the result produced by any of these stages and see if we are on track.
There isn't a toMaybe :: (a -> Bool) -> a -> Maybe a function. I'm not sure why, but with one and using read, the solution is merely:
bounded l = toMaybe (<l) . read
Or using the Safe library...
bounded l = toMaybe (<l) <=< readMay
Which will not throw exceptions if you don't input a string that actually represents a number.
Now, let's say you really do want to write your algorithm iteratively, maybe you need to for performance or it's just one of those algorithms that doesn't readily admit a declarative implementation (there aren't many of those, though). It's still going to be cleaner to use values instead of exceptions, but you need to stop and look at it sometimes.. so what do you do?
Let's write our own iterator function:
data Iter a b c = Next a | Final b | Error c
iterateE :: (a -> Iter a b c) -> a -> ([a], Either c b)
iterateE f = go where
go x = case f x of
Next a -> let (list, final) = go a in (x:list, final)
Final b -> ([x], Right b)
Error c -> ([x], Left c)
This more directly encapsulates stopping the fold early and tracking the intermediate results - even though you can also just stop folds early and track the intermediate results - this is a simpler way to think about it for now. This will provide you with a complete list of all intermediate states and either a result or error that your iterator function can choose to terminate with.
Transforming your solution into this format...
reachBounded limit str = iterateE iter (Just 0,str) where
iter (n, []) = Final n
iter (n, (s:str)) = Next (do
sum <- s
let n = sum * 10 + digitToInt digit
guard (isDigit digit)
guard (n <= limit)
return n, str)
... we don't don't announce any error in this code, but this will let us see what's happened at each step, and also doesn't have a direction in the fold, so you can't get it backwards between left and right.
guys can you help me fix this code?
import Data.Char
import Data.List
-- 1 2 3 4 5 6 7 8
colors = [1,2,3,4,5]
--game :: [Integer] -> [Char]
game a = do
let black = test a color
white = (test2 a color) - black
let x = [a] ++ createScore black white
show x
test [] [] = 0
test (x:xs) (y:ys) = if x == y then 1+test xs ys else 0+test xs ys
test2 a b = length (intersection a b)
intersection first second = [n | n <- first , isInfixOf [n] second]
createScore c b = [(take c (repeat 1) ++ take b (repeat 0))]
start = do
a <- getLine
let b = map read $ words a
--print b
game b
start
I have problem with IO and nonIO functions.
description of the program:
read data from line
convert data to List of int
call function game (which takes List of int like parametr)
make some calculation
print score
start again from 1
Problem is in function start and I have no idea how to fix it.
Thanks for help.
there are a few things that don't quite work with the code you've given us.
I'll work through the compile errors.
On first load we get a name error:
ex1.hs:10:26:
Not in scope: 'color'
Perhaps you meant 'colors' (line 6)
(and again at line 11)
Of course the compiler is correct and we just need to change the appropriate names to match.
Next we get the interesting one I assume you're referring to with regards to IO and non-IO functions:
ex1.hs:28:7:
Couldn't match type '[]' with 'IO'
Expected type: IO Char
Actual type: String
In the return type of a call of 'game'
In a stmt of a 'do' block: game b
In the expression:
do { a <- getLine;
let b = map read $ words a;
game b;
start }
The error is your use of game b in the IO block.
The type annotation you have commented out over the function definition of game is actually correct -- it is [Integer] -> [Char].
As such it's a pure function and we don't need to use the do notation to describe it like we would with something that deals with IO -- because you have used the notation here with an argument of a list, the do expression represents a computation in the context of a list, not an IO computation, so calling it from start has a type mismatch, it expects IO, but it has found [].
We can start fixing it up by turning game into a pure function, using a let-in expression.
game :: [Integer] -> [Char]
game a = let black = test a colors
white = (test2 a colors) - black
x = [a] ++ createScore black white
in show x
So now we have a function that returns the string of the input and it's score.
The compiler now gives the error Expected type: IO Char, Actual type: [Char], this is because we are still trying to use a non-IO expression in the main do block.
We can fix this by actually printing the string to stdout, just using print, so your original
--print b
game b
can just be
print $ game b
At this point the program compiles!
Unfortunately it's still not quite right, when we run this and type in a list of integers like 1 2 3 we get the exception ex1.hs:(14, 1)-(15,66): Non-exhaustive patterns in function test.
This one comes down to your definition of test as:
test [] [] = 0
test (x:xs) (y:ys)
Doesn't account for the possibility of one list being empty -- because the check is always between the head elements of the lists probably the smallest change to fix this could just be:
test (x:xs) (y:ys) = if x == y then 1+test xs ys else 0+test xs ys
test _ _ = 0
And now the program compiles and executes. Hopefully that makes sense.
game is (or should be) a pure function, so you need to call it with a function that returns an IO value in your start function, e. g. print $ game b.
There is also no reason to use do notation there (you can because [a] is also a monad, but you don't take advantage of it in any way). You can just use game a = show x and replace the let statements with a where block (or use let ... in).
test should be able to deal with the case of one list being empty and the other not, or you need to ensure that both lists are always the same size.
I'm pretty brand new to Haskell (only written a fizzbuzz program before the current one) and am trying to write a program that takes the unix wordlist ('/usr/share/dict/words') and prints out the list of anagrams for that word, with any direct palindromes starred. I have the meat of this summed up into one function:
findAnagrams :: [String] -> [(String, [String])]
findAnagrams d =
[x | x <- map (\s -> (s, [if reverse s == t then t ++ "*" else t | t <- d, s /= t && null (t \\ s)])) d, not (null (snd x))]
However, when I run the program I get this output:
abase: babes, bases
abased: debase
abasement: basements
abasements: abatements
abases: basses
And so on, so clearly it isn't working properly. My intention is for the list comprehension to read as follows: for all t in d such that t is not equal to s and there is no difference between t and s other than order, if t is the reverse of s include as t*, otherwise include as t. The problem seems to be with the "no difference between t and s other than order" part, which I'm trying to accomplish by using "null (t \ s)". It seems like it should work. Testing in GHCI gives:
Prelude Data.List> null ("abatements" \\ "abasements")
False
And yet it passes the predicate test. My assumption is that I'm missing something simple here, but I've looked at it a while and can't quite come up with it.
In addition, any notes regarding best practice would be greatly appreciated.
If you break it out into multiple functions (remember, source code size is not really that important), you could do something like:
import Data.List
isPalindrome :: String -> Bool
isPalindrome s = s == reverse s
flagPalins :: [String] -> [String]
flagPalins [] = []
flagPalins (x:xs)
| isPalindrome x = x ++ "*"
| otherwise = x
isAnagram :: String -> String -> Bool
isAnagram s t = (isPalindrome s || s /= t) && ??? -- test for anagram
findAnagrams :: String -> [String] -> [String]
findAnagrams s ws = flagPalins $ filter (isAnagram s) ws
findAllAnagrams :: [String] -> [(String, [String])]
findAllAnagrams ws = filter (not . null . snd) ??? -- words paired with their anagrams
I've intentionally left some holes for you to fill in, I'm not going to give you all the answers ;)
There are only two spots for you to do yourself. The one in findAllAnagrams should be pretty easy to figure out, you're already doing something pretty similar with your map (\s -> ...) part. I intentionally structured isAnagram so it'll return True if it's a palindrome or if it's just an anagram, and you only need one more check to determine if t is an anagram of s. Look at the comment I made on your question for a hint about what to do there. If you get stuck, comment and ask for an additional hint, I'll give you the name of the function I think you should use to solve this problem.
If you really want to make a list comprehension, I would recommend solving it this way, then converting back to a comprehension. In general you should write more verbose code, then compress it once you understand it fully.
Think of a \\ b as "items in a that are not in b."
Consider the implications.
I need to use the functions from Data.Char in order to create a function called camelcaser, which does what sounds like it does: uppercases and lowercases every letter in the pattern upper→lower→upper→lower.
I've attempted to use lexLitChar, to pull out an element then uppercase it, skip the next element until the entire string is completed. I also feel like this could be done with recursion but the type conversions seemed too difficult.
What I was going to do: String -> [(char,string)] -> [char,(char,string)] -> [char] -> String
camelcaser str = foldr f "" str
where f x y = (LexLit x) ++ y
LexLit x = Uppercase (lexlitChar x)
Uppercase (y,z) = toUpper y
I'm kind of stumped on where I'm going with this, anyone care to direct me in the correct path?
Well, if the resulting string is supposed to alternate between uppercase & lowercase, then you can just take the characters of the string two at a time: uppercase the first one, lowercase the second one, and then take the next pair. For example:
camelcaser (a:b:str) = toUpper a : toLower b : camelcaser str
You still need to work out how to handle strings of fewer than two characters, but that should be easy, and this answer has already gone past the basic nudging hint that I wanted it to be.
Here's another possibility. Maybe you can have some fun trying to figure out how it works.
camelCaser = map (\(i,c) -> if even i then toUpper c else toLower c) . zip [0..]