New Line Haskell - haskell

Hey. For a tutorial this week, one of the questions asks to create a function formatLines by using other functions formatLine and formatList, to format a list of lines.
My code looks like this;
type Line = String
formatLine :: Line -> String
formatLine l = l ++ "\n"
formatList :: (a -> String) -> [a] -> String
formatList f [] = []
formatList f xs = f (head xs) ++ formatList f (tail xs)
formatLines :: [Line] -> String
formatLines xs = formatList formatLine xs
The code seems (to me, at least) like it should work, but instead of creating a new line where "\n" is, \n gets appended to the string.
Any help would be greatly appreciated.

That is because you are probably using print to print the result. Instead, use putStr. Observe:
Prelude> print "test\ntest\n"
"test\ntest"
Prelude> putStr "test\ntest\n"
test
test
Other than that, you can use pattern matching to write formatList without head and tail:
formatList :: (a -> String) -> [a] -> String
formatList f [] = []
formatList f (x:xs) = f x ++ formatList f xs
But there is actually no need to define formatList yourself, as it is identical to the function concatMap:
formatList :: (a -> String) -> [a] -> String
formatList = concatMap
Combining all this, you can also just write (note that (++ "\n") is a section):
formatLines :: [String] -> String
formatLines = concatMap (++ "\n")
...which in turn is equivalent to unlines:
formatLines :: [String] -> String
formatLines = unlines

Just try
formatLines = unwords

Related

how can i convert a string to tuple

hello we have to create a code with turns for example "1T3e1s1t" into [(1,'T'),(3,'e'),(1,'s'),(1,'t')]
here is my code
unformat :: String -> [(Int, Char)]
unformat [] = []
unformat (x:xs) = [(unformat' + 1, x)] ++ unformat xss
where
unformat' = length (takeWhile (== x)xs)
xss = drop unformat' xs
it works but the output is "1T3e" -> [(1,'1'),(1,'T'),(1,'3'),(1,'e')]
other than the takeWhile - drop function i get errors. The usage of the function replicate ive tried as well but with the wrong output again
unformat :: String -> [(Int, Char)]
unformat [] = []
unformat (x:xs) = (replicate (fst x) (snd x)) ++ unformat xs
id appreciate any kind of help sincerely
You can pattern-match also by multiple elements at the beginning of a list (like a:b:xs):
module Main where
import Data.Char
main = print $ unformat "1T3e1s1t" -- [(1,'T'),(3,'e'),(1,'s'),(1,'t')]
unformat :: String -> [(Int, Char)]
unformat (i:c:xs) = (digitToInt i, c) : unformat xs
unformat _ = []
Data.Char.digitToInt converts '0' to 0 and 'f' to 15, for example.
Here my solution with foldl. In each step we remember prev chars as a Jsut c if it's the first item of tuple or Nothing if it's second item of tuple.
module Main where
import Data.Char
main :: IO ()
main = print $ unformat "1T3e1s1t"
unformat :: String -> [(Int, Char)]
unformat s = snd $ foldl opr (Nothing , []) s
where
opr (prev, acc) c = case prev of
Just n -> (Nothing, acc ++ [(digitToInt n, c)])
Nothing -> (Just c, acc)
The output will be:
[(1,'T'),(3,'e'),(1,'s'),(1,'t')]

Is there an easier way to write this function and only using prelude from Haskell?

So I'm pretty new to Haskell, and are trying to solve an assignment, I've solved it, but I'm wondering if there is an easier or prettier way to make a function do the same as my wordChange. I'm trying to only use what is already in prelude.
dictionaryChecker _ [] = False
dictionaryChecker word (x:xs) = if elem word (snd x) then True else dictionaryChecker word xs
wordChange :: String -> String
wordChange str = unwords (map (\s -> if length (translate s) > 0 then (translate s)
else if (dictionaryChecker s dictionary) then concat (replicate (length s) "*")
else s) (words str))
translate :: String -> String
translate str = contains str dictionary
contains _ [] = ""
contains str (x:xs) = if elem str (snd x) then fst x else contains str xs
I'd suggest to use the lookup function from Prelude, which takes a key and a list of tuples (a.k.a a dictionary) and returns Maybe value. This simplfies your function a lot. Also, if changeWord uses a dictionary, it should be explicit instead of using a global variable. Below, a partial solution: since it is an assignment I think you should try to complete it ;)
changeWord :: [(String, String)] -> String -> String
changeWord dic s = unwords $ substitute ws
where -- ws is just the list of words s has
ws = words s
-- the function substitute does the word changing recursively. Try to complete it
substitute [] = []
substitute (x:xs) =
case lookup x dic of -- look for x in the dictionary and returns the value if found
Nothing -> undefined --complete
Just y -> undefined --complete
An obfuscated answer: earn a gold star from your professor if you can explain how it works, and be accused of copying from the internet if you can't:
wordChange :: [(String, String)] -> String -> String
wordChange dict = unwords . map (foldr const <*> (`lookup` dict)) . words
Your dictionaryChecker is in essence an any :: Foldable f => (a -> Bool) -> f a -> Bool with elem word . snd as condition:
dictionaryChecker :: (Foldable f, Foldable g, Eq a) => a -> f (b, g a) -> Bool
dictionaryChecker word = any (elem word . snd)
as for a translate, we can work with a section of an infix operator [Haskell-wiki] to make a point-free function:
translate :: String -> String
translate = (`contains` dictionary)
and for contains we can work with a foldr :: Foldable f => (a -> b -> b) -> b -> f a -> b
contains :: (Foldable f, Foldable g, Eq a) => a -> f (String, g a) -> String
contains str = foldr (\x y -> if … then … else …) ""
I leave implementing the … parts as an exercise.

String to List of Int

I would like to incorporate Maybe [int] into this.
The code should take a string and filter out spaces turning it into a list of integers and if their are letters return Nothing.
text2digits :: String -> [Int]
text2digits s = case s of
[] -> []
x:xs
|isDigit x -> digitToInt x :text2digits (filter (/= ' ') xs)
|otherwise -> undefined
input "1233 5687" output: [1,2,3,3,5,6,8,7]
input "a89" required output : Nothing
current output: undefined
I have tried this but it shows up a list of errors
text2digits :: String -> Maybe [Int]
text2digits s = case s of
[] -> Just []
x:xs
|isDigit x -> Just digitToInt x :text2digits (filter (/= ' ') xs)
|otherwise -> Nothing
What is wrong with the code, that you've specified for text2digits :: String -> Maybe [Int]?
The problem is in this line:
digitToInt x :text2digits (filter (/= ' ') xs)
text2digits returns value of Maybe [Int] type, but (:) expects it to be [Int].
In order to fix it, you can use fmap or <$> to apply a function to a structure inside the functor Maybe:
import Data.Char
text2digits :: String -> Maybe [Int]
text2digits s = case s of
[] -> Just []
x:xs
|isDigit x -> ((digitToInt x) :) <$> text2digits (filter (/= ' ') xs)
|otherwise -> Nothing
main = print $ text2digits "1233 5687"
Or probably you can use traverse to refactor the function a bit:
import Data.Char
text2digits :: String -> Maybe [Int]
text2digits s =
traverse digitToMaybeInt $ filter (/= ' ') s
where
digitToMaybeInt x
| isDigit x = Just $ digitToInt x
| otherwise = Nothing
main = print $ text2digits "89"

Filter by length

How I can make here filter (x:xs) = (x, length (x:xs)) that puts length when length > 1?
Currently, if input is abcaaabbb output is [('a',1),('b',1),('c',1),('a',3),('b',3)], but I'm looking for abca3b3.
My code:
import Data.List
encode :: [Char] -> [(Char, Int)]
encode s = map go (group s)
where go (x:xs) = (x, length (x:xs))
main = do
s <- getLine
print (encode s)
Last string will be putStrLn (concat (map (\(x,y) -> x : [y]) (encode s))) for convert list to string.
As I am a newbie myself, this is probably not very haskellian. But you can do it about like this (xs as would be the list [('a', 1), ('b', 2), ('a', 3)]):
Create "a1b2a3":
concat $ map (\(c, l) -> c:(show l)) xs
Filter out 1s:
filter (\x -> x /= '1') "a1b2a3"
will give you "ab2a3"
You can't have a list like this in Haskell:
[('a'),('b'),('c'),('a',3),('b',3)]
Each element if a list needs to have the same type in haskell, and ('c') [('a') :: Char] and ('b',3) [('a',1) :: Num t => (Char, t)] are different types.
Maybe also have a look at List of different types?
I would suggest, that you change your list to a (Char, Maybe num) datastructure.
Edit:
From your new question, I think you have been searching for this:
import Data.List
encode :: [Char] -> [(Char, Int)]
encode s = map go (group s)
where go (x:xs) = (x, length (x:xs))
f :: (Char, Int) -> String
f (a, b) = if b == 1 then [a] else [a] ++ show b
encode2 :: [(Char, Int)] -> String
encode2 [] = []
encode2 (x:xs) = f(x) ++ encode2 xs
main = do
s <- getLine
putStrLn $ encode2 $ encode s
Not sure if this suits your needs, but if you do not need filtering, this does the work:
encode::String -> String
encode "" = ""
encode (x:xs) = doIt0 xs x 1 where
doIt0 [] ch currentPos = [ch]++showPos currentPos
doIt0 (x:xs) ch currentPos
|x==ch = doIt0 xs ch $ currentPos+1
|otherwise= [ch]++ (showPos currentPos) ++ (doIt0 xs x 1)
showPos pos = if pos> 1 then show pos else ""
main = do
s <- getLine
print (encode s)

Haskell delete Chars from String

I'm trying to write a function of the form
f :: String -> [String]
f str = ...
that returns the list of all the strings formed by removing exactly one character from str. For example:
ghci> f "stack"
["tack","sack","stck","stak","stac"]
Because String and [Char] are synonymous, I could use the index, but I know that you should avoid doing that in Haskell. Is there a better way besides using the index?
You could use recursion like so:
f :: [a] -> [[a]]
f [] = []
f (s:ss) = ss : map (s:) (f ss)
The Josh Kirklin's solution as a one-liner:
f = tail . foldr (\x ~(r:rs) -> (x : r) : r : map (x :) rs) [[]]
Maybe a more readable way to describe it is:
gaps :: [a] -> [[a]]
gaps xs = zipWith removeAt [0..] $ replicate (length xs) xs
removeAt i xs = ys ++ zs
where
(ys,_:zs) = splitAt i xs
But practically, it is slower than the other solutions.

Resources