Haskell - how to change the show to get the right output - haskell

I have defined these datatypes which I am trying to create and print now.
type TNonTerminal = String -- will be creating own ones where [A-Z] won't be enough
type TTerminals = Char
type TSymbols = String -- both terminals and nonterminals
data Rule = Rule
{ leftSide :: TNonTerminal
, rightSide :: [TSymbols]
} deriving (Eq)
data RightLinearGrammar = RLG
{ nonterminals :: [TNonTerminal]
, terminals :: [TTerminals]
, firstNonterminal :: TNonTerminal
, rules :: [Rule]
} deriving (Eq)
So I also created those Show instances
instance Show Rule where
show (Rule ls rs) = show ls ++ "->" ++ show rs ++ "\n"
instance Show RightLinearGrammar where
show (RLG n t fn r) = show n ++ "\n" ++ show t ++ "\n" ++ show fn ++ "\n" ++ show r ++ "\n"
And I get this output (for clarification I created the Type RightLinearGrammar and called putStr $ show rlg):
["A","B"] -- [TNonTerminal]
"abc" -- [TTerminals]
"A" -- TNonTerminal
["A"->["aaB"] --
,"A"->["ccB"]
,"B"->["bB"] -- [Rule]
,"B"->["#"]
] --
How should I change the code to get better output like this one?
A,B
a,b,c
A
A->aaB
A->ccB
B->bB
B->#

show is by default going to give you quotes around strings and brackets around lists. If you just go back to concatenating strings and joining lists with commas or newlines, you should get the output you're expecting:
import Data.List (intercalate)
instance Show Rule where
show (Rule ls rs) = ls ++ "->" ++ intercalate "," rs
instance Show RightLinearGrammar where
show (RLG n t fn r) = intercalate "," n ++ "\n" ++ t ++ "\n" ++ fn ++ "\n" ++ (intercalate "\n" $ map show r) ++ "\n"

You either need to replace your type synonyms with newtypes, and define show on them to do what you want, or more likely replace the calls to show in your instances with calls to a custom formatter function.
Note: show is really not the right function for what you're trying to do, since it usually produces output you could paste back into ghci and arguably should be limitted to that use. You could easily define your own function and use it like this:
formatRule :: Rule -> String
formatRule (Rule ls rs) = ls ++ "->" ++ concat (intersperse "," rs) ++ "\n"
formatRightLinearGrammar :: RightLinearGrammar -> String
formatRightLinearGrammar (RLG n t fn r) =
concat (intersperse "," n) ++ "\n"
++ intersperse ',' t ++ "\n"
++ fn ++ "\n"
++ concat (map formatRule r)
Note: this is going to be fairly inefficient for large grammars; you might want to consider re-writing it as
formatRule :: Rule -> String -> String
formatRule (Rule ls rs) = (ls++) . ("->"++) . concatDS (intersperse "," rs) . ("\n"++)
formatRightLinearGrammar :: RightLinearGrammar -> String
formatRightLinearGrammar (RLG n t fn r) =
concatDS (intersperse "," n) $ ("\n"++) $
(intersperse ',' t ++) $ ("\n"++) $
(fn++) $ ("\n"++) $
foldr formatRule "" r
concatDS ss s' = foldr (++) s' ss

Related

How to show a list of values as a comma-separated list of strings in Haskell

I need to remove a comma from the last list after writingFile. How to specifically remove the comma from list?
Expected output
[Spa"24""Meta""Outram"1[("Colin",4),("Nixon",6),("Sam",5),("Petrina",7),("Justin",5)],
Spa"25""Zen""Outram"2[("Bryan",7),("Petrina",9),("Colin",4),("Nixon",7),("Sam",5)]]
Output achieved
[Spa"24""Meta""Outram"1[("Colin",4),("Nixon",6),("Sam",5),("Petrina",7),("Justin",5)],
Spa"25""Zen""Outram"2[("Bryan",7),("Petrina",9),("Colin",4),("Nixon",7),("Sam",5)],]
-- turn original spa into String
spaStr :: Spa -> String
spaStr spa#(Spa rid br ar st s)
= "Spa" ++ "\""++ rid ++ "\"" ++ "\"" ++ br ++ "\""++ "\""++ ar ++ "\"" ++ show st ++ show s ++ "," ++"\n"
let newdB = ((addTspa rid br ar (read st))dB)
putStrLn $ spaStr newdB
writeFile "spa.txt" ("[" ++ spaStr newdB ++ "]")
As suggested in the comments, a possible approach is:
Turn your [Spa] to [String] without adding any commas or newlines. This will generate a list like ["aaa", "bbb", "ccc"]
Use intersperse ",\n" on that list to add that separator in the middle of adjacent elements. This will not add the separator to the end. You will get something like ["aaa", ",\n", "bbb", ",\n", "ccc"]
Use concat on the result, obtaining "aaa,\nbbb,\nccc".
Add the final [ and ].
Here is an example which runs and produces your wanted output:
import Data.List (intersperse)
data Spa = Spa String String String Int [(String, Int)]
-- turn original spa into String
spaStr :: Spa -> String
spaStr spa#(Spa rid br ar st s)
= "Spa" ++ "\""++ rid ++ "\"" ++ "\"" ++ br ++ "\""++ "\""++ ar ++ "\"" ++ show st ++ show s
newDb :: [Spa]
newDb =
[ Spa "24" "Meta" "Outram" 1
[("Colin",4),("Nixon",6),("Sam",5),("Petrina",7),("Justin",5)]
, Spa "25" "Zen" "Outram" 2
[("Bryan",7),("Petrina",9),("Colin",4),("Nixon",7),("Sam",5)]
]
main :: IO ()
main =
putStrLn $ "[" ++ concat (intersperse ",\n" $ map spaStr newDb) ++ "]"
Output:
[Spa"24""Meta""Outram"1[("Colin",4),("Nixon",6),("Sam",5),("Petrina",7),("Justin",5)],
Spa"25""Zen""Outram"2[("Bryan",7),("Petrina",9),("Colin",4),("Nixon",7),("Sam",5)]]

Haskell - Iterate Tuple with Different Functions

I have been trying to to iterate the cand data in order to apply a function "pt_string".
Pt :: (Float, Float)
Person :: (Pt, Pt, [Pt], Float)
My idea is to call that function "pt_string" in a different way for each element of the tupple.
For example:
pt_string Point (first)
map pt_string [Point]
pt_string Point (second)
show "Tmp"
So far, I got:
pt_string :: pt -> String
pt_string pt = "(" ++ show (fst pt) ++ "," ++ show (snd pt) ++ ")\n"
Which works fine. But how can I create cand_to_string :: cand -> String in the above order?
Thanks!
Assuming
type Candidate = (Point, Point, [Point], Float)
you can use
candidate_to_string :: Candidate -> String
candidate_to_string (p1, p2, ps, f) =
"(" ++
point_to_string p1 ++ ", " ++
point_to_string p2 ++ ", " ++
points_to_string ps ++ ", " ++
show f ++
")"
which relies on
points_to_string :: [Point] -> String
points_to_string ps = "[" ++ intercalate ", " (map point_to_string ps) ++ "]"
exploiting Data.List.intercalate to add commas between the points.
Also note that, if you simply want the standard list/tuple printing format, you can directly use
candidate_to_string :: Candidate -> String
candidate_to_string = show

how to return one string as multiple lines in IO?

I'm trying to create a show' function that will take a list of tuples and return a string that creates a new line after every tuple. So it would take [(x,y),(a,b),(c,d)] and return
x y
a b
c d
what I have so far in terms of code is
show' :: [(String,Int)] -> String
show' [] = ""
show' (x:xs) = (fst x) ++ " " ++ (show (snd x)) ++ " something that will create a newline in IO " ++ show' xs
Newlines in Haskell can be represented with "\n".
If you are still printing strings to the console that contain the character sequence \n, it probably means you are using print. Use putStr or putStrLn instead.
You don't want to use print because it internally uses show, which encodes the literal newline as the characters \ and n. print is more useful in debugging than in actual production code.
There is function unlines which joins lines and appends newline to each of them. So you can use something like
show' :: [(String, Int)] -> String
show' xs = unlines $ map (\(x,y) -> (show x) ++ " " ++ (show y)) xs
Use putStrLn to print the output otherwise you'll see '\n' instead of newline.

Printing the values inside a tuple in Haskell

I have a list of tuples. For example: [("A",100,1),("B",101,2)]. I need to display it in a simple way. For example: "your name is: A", "Your id is: 100".
If anyone can find a solution for this, it would be a great help. Thanks in advance.
The easiest way to do this is to create a function that works for one of the elements in your list. So you'll need something like:
showDetails :: (String, Int, Int) -> String
showDetails (name, uid, _) = "Your name is:" ++ name ++ " Your ID is: " ++ show uid
Then you would apply this function to each element in the list, which means you want to use the mapping function:
map :: (a -> b) -> [a] -> [b]
So, if your list is called xs, you would want something like:
map showDetails xs
This obviously gives you a result of type [String], so you might be interested in the unlines function:
unlines :: [String] -> String
This simply takes a list of strings, and creates a string where each element is separated by a new line.
Putting this all together, then, gives you:
main :: IO ()
main = putStrLn . unlines . map showDetails $ [("A",100,1),("B",101,2)]
For a single tuple, just pattern match all the elements, and do something with them. Having a function that does that, you can use map to transform the entire list.
import Data.List (foldl')
show_tuple :: (Num a, Num b) => (String, a, b) -> String
show_tuple (name, id, something) =
"Your name is: " ++ name ++ "\n" ++
"Your ID is: " ++ (show id) ++ "\n" ++
"Your something: " ++ (show something) ++ "\n\n"
-- transforms the list, and then concatenates it into a single string
show_tuple_list :: (Num a, Num b) => [(String, a, b)] -> String
show_tuple_list = (foldl' (++) "") . (map show_tuple)
The output:
*Main Data.List> putStr $ show_tuple_list [("ab", 2, 3), ("cd", 4, 5)]
Your name is: ab
Your ID is: 2
Your something: 3
Your name is: cd
Your ID is: 4
Your something: 5
Quick and dirty solution
f (x,y,z) = "your id is " ++ (show y) ++ ", your name is " ++ (show x) ++ "\n"
main = putStrLn $ foldr (++) "" (map f [("A",100,1),("B",101,2)])
OR (by #maksenov)
main = putStrLn $ concatMap f [("A",100,1),("B",101,2)]
Please try:
get1st (a,_,_) = a
get2nd (_,a,_) = a
get3rd (_,_,a) = a
showTuples [] = ""
showTuples (x:xs) = "Your name is:" ++ show(get1st(x)) ++ " Your ID is: " ++ show(get2nd(x)) ++ "\n" ++ showTuples xs
main = do
let x = [("A",100,1),("B",101,2)]
putStrLn . showTuples $ x

Moving To New Line In Haskell - Updated

I have the following functions in Haskell that must print the sales of weeks. Each sale in a new line. But it is not working the way i expect it to. The problem i have is the newline character '\n'.
Code:
printWeeks :: Int->String
printWeeks 0 = printWeek 0
printWeeks x = printWeeks(x-1) ++ printWeek x
printWeek :: Int->String
printWeek x = show(x) ++ " " ++ stars (sales x) ++ "'\n'"
I have tried many ways but the new line character is not working as expected. Everything is printed on the same line whichis not what i want.
Need help?
thanks
UPDATE
The following is not working because of compile errors. The errors comes from the second line of formatLines. The type decalaration is causing errors. Need help here
formatLine :: (Name,Price)->IO()
formatLine (a,b) = putStrLn (a ++ dots ++ p)
where
x=(length a)
p=(formatPence b)
y=length p
z=lineLength-(x+y)
dots = printDots z
formatLines :: [(Name,Price)]->IO()
formatLines []= ""
formatLines (a:x) = formatLines x ++ formatLine a
You should use ++ "\n" to append a newline to the output; your current code will add a ', then a newline, then another '.
As #marcog points out, be sure to use putStr to print it out (or don't append the newline at all and use putStrLn). Example:
Hugs> putStr (show 4 ++ "\n")
4
Hugs> putStrLn (show 4 ++ "\n")
4
Hugs> print (show 4 ++ "\n")
"4\n"
(Note that the Hugs interpreter adds extra newlines after each output.)
You are probably printing the string using print x, which is equivalent to putStrLn (show x). show x is converting the newlines into readable characters \ and n. You need to use putStrLn x instead, or putStr x if you don't want to append a newline to the end of the string.
You should also remove the single quotes you have around the newline, unless that was intentional.
It's a bit of a riddle why so much action is happening under the heading of IO. This is maybe a little verbose. I couldn't tell where lineLength was coming from so I made it a parameter.
formatLine :: Int -> (Name,Price) -> String
formatLine linelength (name, price) = name ++ dotfill ++ showprice
where
showprice :: String
showprice = formatPence price
extra :: Int
extra = linelength - length (name ++ showprice)
dotfill :: String
dotfill = replicate extra '.'
formatLines :: Int -> [(Name, Price)] -> String
formatLines linelength []= ""
formatLines linelength (first:rest) =
(formatLine linelength first ++ "\n") ++ formatLines linelength rest
standardPrint :: [(Name, Price)] -> IO ()
standardPrint listing = putStrLn (formatLines 50 listing)
fileAwayPrices :: FilePath -> [(Name,Price)] -> IO()
fileAwayPrices filename listing = writeFile filename (formatLines 70 listing)
testlist :: [(Name,Price)]
testlist = [("oats",344),("barley", 299),("quinoa",599)]
-- *Main> standardPrint testlist
-- oats...........................................344
-- barley.........................................299
-- quinoa.........................................599
type Name = String
type Price = Integer
formatPence n = show n
Re your update: your type declaration is correct, it's the rest of formatLines that's wrong.
formatLines :: [(Name,Price)]->IO()
formatLines [] = return ()
formatLines (a:x) = formatLines x >> formatLine a
A more concise way of writing that is
formatLines :: [(Name,Price)]->IO()
formatLines = mapM_ formatLine . reverse

Resources