Haskell : list comprehension - haskell

How can I write this code without repeating (c !! x) where is x = [0..7], using list comprehension is better but I couldn't figure how to write it
show (EOBoard f c r) = "EOBoard\n" ++ "Foundations "
++ show f ++ "\n" ++ "Columns \n"
++ show (c!!0) ++ "\n" ++ show (c!!1) ++ "\n"
++ show (c!!2) ++ "\n" ++ show (c!!3) ++ "\n"
++ show (c!!4) ++ "\n" ++ show (c!!5) ++ "\n"
++ show (c!!6) ++ "\n" ++ show (c!!7) ++ "\n"
++ "Reserves " ++ show r

Let's start by getting rid of all those manual line breaks.
import Data.List
show (EOBoard f c r) = intercalate "\n" $
[ "EOBoard"
, "Foundations " ++ show f
, "Columns"
, show (c!!0)
, show (c!!1)
, show (c!!2)
, show (c!!3)
, show (c!!4)
, show (c!!5)
, show (c!!6)
, show (c!!7)
, "Reserves " ++ show r]
Note: if you want a line break at the end too, use unlines instead of intercalate.
As you noticed, there's a rather repetitive section. Also, those !! applications are kind of expensive. A list comprehension solves both problems, but I'd use map instead.
show (EOBoard f c r) = intercalate "\n" $
[ "EOBoard"
, "Foundations " ++ show f
, "Columns" ] ++
map show c ++
["Reserves " ++ show r]
(map show c could be replaced by [show x | x <- c] if you prefer.)
There's still something funny; Show really isn't for pretty-printing. show shouldn't insert line breaks. You probably actually want to write a custom pretty-printing function instead.

What you wrote is equivalent to
show (EOBoard f c r) = "EOBoard\n" ++ "Foundations "
++ show f ++ "\n" ++ "Columns \n" ++
++ concat [ show s ++ "\n" | s <- take 8 c]
++ "\n" ++ "Reserves " ++ show r
which is equivalent to
show (EOBoard f c r) = "EOBoard\n" ++ "Foundations "
++ show f ++ "\n" ++ "Columns \n" ++
++ [ ch | s <- take 8 c, ch <- show s ++ "\n" ]
++ "\n" ++ "Reserves " ++ show r
or, using the concat more instead of inlining it, it is equivalent to
show (EOBoard f c r) = concat (
[ "EOBoard\n", "Foundations ", show f, "\n", "Columns \n" ]
++ [ show s ++ "\n" | s <- take 8 c]
++ ["\n" , "Reserves " , show r] )
which is normally written with the $ operator, as
show (EOBoard f c r) = concat $
[ "EOBoard\n", "Foundations ", show f, "\n", "Columns \n" ]
++ [ show s ++ "\n" | s <- take 8 c]
++ ["\n" , "Reserves " , show r]

Related

Print Custom String With the Sum of Three Lists

When I run the following code:
main = do
let smallTriplets xs ys zs = [ (x, y, z) | x <- xs, y <- ys, z <- zs, let sum = x + y + z, sum <= 10]
print (smallTriplets [1,2] [3,4] [5,6])
It gives the output:
[(1,3,5),(1,3,6),(1,4,5),(2,3,5)]
However, I want smallTriplets to print the sum with some customization. To be more specific, it will be better if I could replace let sum = x + y + z in smallTriplets with some code so that it gives output like:
Sum is 1+3+5 = 9
Sum is 1+3+6 = 10
Sum is 1+4+5 = 10
Sum is 2+3+5 = 10
I am expecting that code to have a string like "Sum is " ++ show(x) ++ "+" ++ show(y) ++ "+" ++ show(z) ++ " = " ++ show(x + y + z) ++ "\n"
How can I do that?
If this is not possible then please show me how to print:
Sum is 1+3+5 = 9
Sum is 1+3+6 = 10
Sum is 1+4+5 = 10
Sum is 2+3+5 = 10
What's the problem of writing "Sum is " ++ show x ++ "+" ++ show y ++ "+" ++ show z ++ " = " ++ show sum instead of (x, y, z)?
You could like this
main = do
let helper x y z s = "Sum is " ++ show x ++ "+" ++ show y ++ "+" ++ show z ++ " = " ++ show s
let smallTriplets xs ys zs = [ helper x y z sum | x <- xs, y <- ys, z <- zs, let sum = x + y + z, sum <= 10]
putStr $ unlines $ smallTriplets [1,2] [3,4] [5,6]
Here, smallTriplets returns a [String] instead of a [(,,)]; unlines converts the [String] to a String with embedded \ns; finally, putStr prints the string.
This is a quick and dirty solution which alters the function smallTriplets, so if you planned to use the original result in other ways this solution will not help.
If your aim is to log things you do, then a more complex solution is needed. Maybe using the writer monad?

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 - how to change the show to get the right output

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

Haskell non-exhaustive patterns and converting function output

I have a function that wants to list all of a dataType.
fn [] = []
fn (dt#(DataType t d y [(f,r)]):dts) = ["T:" ++ t ++ " D: " ++ d ++ " R: " ++ show y ++ "Ra" ++ show (fnAvg dt)] ++ fn dts
Where t and d are strings, y is an int, f is a string and r is an int (not sure f and r matter though, will explain furthur on).
I got the error non-exhaustive patterns, and presumed it was because I didn't have one for when there was only one element in the list, so I added this between the other patterns:
fn [dt#(DataType t d y [(f,r)])] = ["T:" ++ t ++ " D: " ++ d ++ " R: " ++ show y ++ "Ra" ++ show (fnAvg dt)]
It compiled, but when I called the function it once again told me 'non-exhaustive patterns'. I'm struggling to think of what pattern i've missed, should I add a wildcard pattern afterward to catch everything? I'm not looking for someone to type out the answer, but hints or suggestions are welcome.
The pattern [(f,r)] only matches when the list contains one element. If it contains zero, or two, or any other number, you have a pattern match failure.
What the code should do in this instance, I couldn't say...
You do already cover the one-element-list case: that matches fn (dt#(DataType t d y [(f,r)]):[]), since dts can be anything including the empty list.
Indeed there's no reason to use explicit recursion here: you basically have something like
f [] = []
f (x:xs) = g x ++ f xs
Compare that to the monad instance of lists:
instance Monad [] where
return x = [x]
[] >>= _ = []
(x:xs) >>= g = g x ++ (xs >>= g)
So you should write you function as
fn l = l >>= \dt#( DataType t d y [(f,r)] )
-> ["T:" ++ t ++ " D: " ++ d ++ " R: " ++ show y ++ "Ra" ++ show (fnAvg dt)]
That won't fix your problem though, but it makes it obvious what's going on: evidently, DataType t d y [(f,r)] is not the only valid pattern for that type. As MathematicalOrchid points out, [(f,r)] matches only a list with length 1, but you need to cover other lengths as well.
fn l = l >>= \dt -> case dt of
DataType t d y [(f,r)]
-> ["T:" ++ t ++ " D: " ++ d ++ " R: " ++ show y ++ "Ra" ++ show (fnAvg dt)]
DataType t d y []
-> ["Some other stuff"]
DataType t d y [(f,r), ...]
-> ["Yet other stuff"]
or whatever.
Indeed, if you only ever return [ ("stuff") ] here, then you're not really using the monadic bind functionality at all: you could have written the recursive version without ++, only reconstruct the thing with :, and in fact you have simply a map operation:
fn = map $ \dt -> case dt of
DataType t d y [(f,r)]
-> "T:" ++ t ++ " D: " ++ d ++ " R: " ++ show y ++ "Ra" ++ show (fnAvg dt)
DataType t d y []
-> "Some other stuff"
DataType t d y [(f,r), ...]
-> "Yet other stuff"

Newline in Haskell String?

How can I create a newline inside a String? Is it possible without using IO ()?
formatRow :: Car -> String
formatRow (a, d:ds, c, x:xs) = a ++ " | " ++ x ++ concat xs ++ " | " ++ show c ++ " | " ++ d ++ concat ds ++ (show '\n')
To create a string containing a newline, just write "\n".
If you run your program on Windows, it will automatically be converted to "\r\n".
Note that calling show on it will escape the newline (or any other meta-characters), so don't do foo ++ (show "\n") or foo ++ (show '\n') - just use foo ++ "\n".
Also note that if you just evaluate a string expression in GHCi without using putStr or putStrLn, it will just call show on it, so for example the string "foo\n" will display as "foo\n" in GHCi, but that does not change the fact that it's a string containing a newline and it will print that way, once you output it using putStr.

Resources