Question

Hi I have small problem with printing [[[Char]]] using IO.

I have a [[[Char]]] list - in another words [[String]]

I would like to print them like

FirstStringOfFirstListOfStrings
SecondStringOfFirstListOfStrings
.
.
.
LastStringOffFirstListofStrings
(some gap anything empty line "----" string anything)
FirstStringOfSecondsListOfStrings

In another words I don't want the [String] to be printed one under another (one row break at least or some line filled with ------ for example)

mapM_ (mapM_ print) 

My idea was to compose the (mapM_ print) function with another but that doesn't seem to work. Any suggestions?

Edit: Here's an example, as requested

data to print: [["abc","cde","efg"],["hjk","mno","prs"],["stu","wxy","zzz"]]

while using mapM_ (mapM_ print) it prints like

abc
cde
efg
hjk
mno
prs
stu
wxy
zzz

But I would like it to be printed like this:

abc
cde
efg

hjk
mno
prs

stu
why
zzz

They can be separated by an empty line or some string.

Était-ce utile?

La solution

Lets start with types. What you have is [[String]] and what you want is [[IO ()]] i.e each String gets converted to a IO () action (to print it). Lets try to achieve this:

strToPrint :: [[String]] -> [[IO ()]]
strToPrint strs = map (map putStrLn) strs                

Now we want [[IO ()]] to flatten to get [IO ()] where we insert the desired line break print action as well while flattening the list.

flattenActions :: [[IO ()]] -> [IO ()]
flattenActions actions = concat $ intersperse [(putStrLn "")]  actions

Now we can execute these [IO ()] action one by one using sequence_. It seems we can combine all these functions into one function using functional composition and hence we get

printStrings :: [[String]] -> IO ()
printStrings  = sequence_ . flattenActions . strToPrint

And use it like:

main = printStrings [["a","b","c"],["d","e","f"]]

You can then look for making this more generic and refactor it accordingly.

Autres conseils

With your example the code, for any separator 'sep' is then:

import Data.List (intercalate)

let x = [["abc","cde","efg"],["hjk","mno","prs"],["stu","wxy","zzz"]]

p sep = putStr . unlines . intercalate sep

main = do
    p "" x
    p "---" x

Remember that mapM_ takes a function that returns a monadic action as its parameter. That action can be composed using the normal monadic operations.

So in your code sample you have:

printStrings :: [[String]] -> IO ()
printStrings = mapM_ (mapM_ putStrLn)

Whereas you would like to have:

printStrings :: [[String]] -> IO ()
printStrings = mapM_ (\xs -> (mapM_ putStrLn) xs >> putStrLn ""))

So what we do is to replace the inner mapM_ putStrLn with a function: \xs -> mapM_ (putStrLn xs >> putStrLn "") that also prints out the separator.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top