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
Related
I do have the following code:
suffixes :: [a] -> [[a]]
suffixes [] = [[]]
suffixes l#(_:t) = l : suffixes t
prefixes :: [a] -> [[a]]
prefixes [] = [[]]
prefixes l#x = l : prefixes (init x)
menu :: Char -> [a] -> Either String [[a]]
menu 'p' l = Right (prefixes l)
menu 's' l = Right (suffixes l)
menu x _ = Left ("(" ++ show x ++ ")" ++ "is not supported, use (p)refix or (s)uffix")
I do have the following test function:
testMenuP = "Expected Right [[1,2],[1],[]]; menu 'p' [1,2] returned " ++ show (menu 'p' [1,2] :: Either String [[Int]])
testMenuS = "Expected Right [[1,2],[2],[]]; menu 's' [1,2] returned " ++ show (menu 's' [1,2] :: Either String [[Int]])
testMenuC = "Expected Left \"(d) is not supported, use (p)refix or (s)uffix\"; menu 'd' [1,2] returned " ++ show (menu 'd' [1,2] :: Either String [[Int]])
testMenu = putStr (testMenuP ++ "\n" ++ testMenuS ++ "\n" ++ testMenuC ++ "\n")
My question is now, how do I get rid of the quotes '' in the Char 'd' when I output the string (as shown in the test function testMenuC).
You can replace the part of menu with:
menu x _ = Left ("(" ++ [x] ++ ")" ++ "is not supported, use (p)refix or (s)uffix")
or even
menu x _ = Left . mconcat $ ["(", [x], ")", "is not supported, use (p)refix or (s)uffix"]
For my first line of Haskell I thought it'd be a nice case to produce a "natural listing" of items (of which the type supports show to get a string representation). By "natural listing" I mean summing up all items separated with , except the last one, which should read and lastitem. Ideally, I'd also like to not have a , before the "and".
To spice it up a bit (to show off the compactness of haskell), I wanted to have an "inline" solution, such that I can do
"My listing: " ++ ... mylist ... ++ ", that's our listing."
(Obviously for "production" making a function for that would be better in all ways, and allow for recursion naturally, but that's the whole point of my "inline" restriction for this exercise.)
For now I came up with:
main = do
-- hello
nicelist
nicelist = do
let is = [1..10]
putStrLn $ "My listing: " ++ concat [ a++b | (a,b) <- zip (map show is) (take (length is -1) $ repeat ", ") ++ [("and ", show $ last is)]] ++ ", that's our listing."
let cs = ["red", "green", "blue", "yellow"]
putStrLn $ "My listing: " ++ concat [ a++b | (a,b) <- zip (map show cs) (take (length cs -1) $ repeat ", ") ++ [("and ", show $ last cs)]] ++ ", that's our listing."
but this hardly seems optimal or elegant.
I'd love to hear your suggestions for a better solution.
EDIT:
Inspired by the comments and answer, I dropped the inline requirement and came up with the following, which seems pretty sleek. Would that be about as "haskellic" as we can get, or would there be improvements?
main = do
putStrLn $ "My listing: " ++ myListing [1..10] ++ ", that's the list!"
putStrLn $ "My listing: " ++ myListing ["red", "green", "blue", "yellow"] ++ ", that's the list!"
myListing :: (Show a) => [a] -> String
myListing [] = "<nothing to list>"
myListing [x] = "only " ++ (show x)
myListing [x, y] = (show x) ++ " and " ++ (show y)
myListing (h:t) = (show h) ++ ", " ++ myListing t
Here's how I would write it:
import Data.List
niceShow' :: [String] -> String
niceShow' [] = "<empty>"
niceShow' [a] = a
niceShow' [a, b] = a ++ " and " ++ b
niceShow' ls = intercalate ", " (init ls) ++ ", and " ++ last ls
niceShow :: [String] -> String
niceShow ls = "My listing: " ++ niceShow' ls ++ ", that's our listing."
niceList :: IO ()
nicelist = do
putStrLn $ niceShow $ show <$> [1..10]
putStrLn $ niceShow ["red", "green", "blue", "yellow"]
Steps:
Create niceShow to create your string
Replace list comprehensions with good old function calls
Know about intercalate and init
Add type signatures to top levels
Format nicely
niceShow can only be inlined if you know the size of the list beforehand, otherwise, you'd be skipping the edge cases.
Another way to state the rules for punctuating a list (without an Oxford comma) is this:
Append a comma after every element except the last two
Append “and” after the second-to-last element
Leave the final element unchanged
This can be implemented by zipping the list with a “pattern” list containing the functions to perform the modifications, which repeats on one end. We want something like:
repeat (<> ",") <> [(<> " and"), id]
But of course this is just an infinite list of the comma function, so it will never get past the commas and on to the “and”. One solution is to reverse both the pattern list and the input list, and use zipWith ($) to combine them. But we can avoid the repeated reversals by using foldr to zip “in reverse” (actually, just right-associatively) from the tail end of the input. Then the result is simple:
punctuate :: [String] -> [String]
punctuate = zipBack
$ [id, (<> " and")] <> repeat (<> ",")
zipBack :: [a -> b] -> [a] -> [b]
zipBack fs0 = fst . foldr
(\ x (acc, f : fs) -> (f x : acc, fs))
([], fs0)
Example uses:
> test = putStrLn . unwords . punctuate . words
> test "this"
this
> test "this that"
this and that
> test "this that these"
this, that and these
> test "this that these those them"
this, that, these, those and them
There are several good ways to generalise this:
zipBack is partial—it assumes the function list is infinite, or at least as long as the string list; consider different ways you could make it total, e.g. by modifying fs0 or the lambda
The punctuation and conjunction can be made into parameters, so you could use e.g. semicolons and “or”
zipBack could work for more general types of lists, Foldable containers, and functions (i.e. zipBackWith)
String could be replaced with an arbitrary Semigroup or Monoid
There’s also a cute specialisation possible—if you want to add the option to include an Oxford comma, its presence in the “pattern” (function list) depends on the length of the final list, because it should not be included for lists of 2 elements. Now, if only we could refer to the eventual result of a computation while computing it…
I got a data type "Cake"
*data Cake = EmptyBox | Food { name :: String
, flavors :: [Cake]} deriving( Read, Eq)*
instance Show Cake where
show cake = prints cake where
prints (Food name []) = name ++ "\n"
I want to print cakes like this (each cake got different flavors)
Cake1
Chocolate
Nutella
Strawberry
Cake2
Chocolate
Vanilla
Cake3
But I got error, doesn't works! How can I do it?
Since prints :: Cake -> String and flavors :: [Cake], we know
map prints flavors :: [String]
But this doesn't jive with the context in which it is used, since in
" " ++ expr
the " " is clearly a String and we therefore expect expr to be a String and not a [String].
To fix this, you should write or find a function which converts your [String] into a String in some way -- there are many candidate behaviors for this type, so you should decide what behavior you want and then find a way to achieve that behavior.
For test data, I used:
cake1 = Food{name="Cake1"
,flavors=[Food{name="Chocolate"
,flavors=[]}
,Food{name="Nutella"
,flavors=[Food{name="Strawberry"
,flavors=[]}]}]}
cake2 = Food{name="Cake2"
,flavors=[Food{name="Chocolate"
,flavors=[]}
,Food{name="Vanilla"
,flavors=[]}]}
cake3 = Food{name="Cake3"
,flavors=[]}
and wrote Show Cake as unlines composed with a function that has type Cake -> [String]
instance Show Cake where
show = unlines . prints where
prints :: Cake -> [String]
prints EmptyBox = []
prints (Food s []) = [s] -- a Food with no subflavors
prints (Food s fs) = s:concatMap (map (" "++) . prints) fs
That last line handles the general case of a Food with name s and flavors fs by mapping prints over fs, then mapping (" "++) over each sublist in that map, and concat'ing them together.
(" "++) :: String -> String
map (" "++) :: [String] -> [String]
map (" "++) . prints :: Cake -> [String]
map (map (" "++) . prints) :: [Cake] -> [[String]]
concat . map (map (" "++) . prints) :: [Cake] -> [String]
concatMap = concat . map
concatMap (map (" "++) . prints) :: [Cake] -> [String]
Each successive level of flavors adds an extra level of indentation (that's what the (" "++) is for). We can test it:
TestModule> putStrLn $ concatMap show [cake1, cake2, cake3]
Cake1
Chocolate
Nutella
Strawberry
Cake2
Chocolate
Vanilla
Cake3
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
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