Reputation: 475
Just started learning Haskell a few days ago and I've come across a few issues. The first issue deals with printing a list of numbers. The desired behavior is as follows:
input: [1,2,3,4,5,6]
output: 1 2 3 | 4 5 6
So its a simple concept, I just need to output the elements of a list with the "|" symbol inserted between every three numbers, but I can't for the life of me figure it out. It seems like most of the stuff I've tried involves strings and even if I were able to get the list to strings such as ["1", "2", "3", ...] all the methods I've tried print the numbers each on their own line which is not what I need.
Any help would be greatly appreciated.
Upvotes: 0
Views: 1723
Reputation: 139840
Using the split package (recently added to the Haskell Platform):
> import Data.List -- for intercalate
> import Data.List.Split -- for chunksOf
> intercalate " | " . map unwords . chunksOf 3 $ map show [1..7]
"1 2 3 | 4 5 6 | 7"
Relevant documentation: chunksOf
, unwords
, intercalate
.
Upvotes: 5
Reputation: 183858
The first part is the easiest, you need to convert the numbers to String
s,
format :: (Num a, Show a) => [a] -> String
format xs = result
where
strings = map show xs
does that. Then we need to split any list into chunks of three (more general, n
) elements. splitAt
splits a list into a front part of the desired number of elements - if the list is long enough - and a remainder. Iterating the procedure on the remainder, while that is not empty leads to the desired result.
chunk :: Int -> [a] -> [[a]]
chunk _ [] = []
chunk n xs = ys : chunk n zs
where
(ys, zs) = splitAt n xs
That is a recurring pattern, so there is a combinator for that, and we could also write
import Data.List (unfoldr)
chunk :: Int -> [a] -> [[a]]
chunk n = unfoldr split
where
split [] = Nothing
split xs = Just $ splitAt n xs
So we can continue our format
,
format :: (Num a, Show a) => [a] -> String
format xs = result
where
strings = map show xs
chunks = chunk 3 strings
Then we need to insert a "|"
between all chunks, that is done by intercalate
from Data.List
, and finally, concatenate all strings with spaces between them, that's what unwords
does, so
format :: (Num a, Show a) => [a] -> String
format xs = result
where
strings = map show xs
chunks = chunk 3 strings
result = unwords $ intercalate "|" chunks
Or
format = unwords . intercalate "|" . chunk 3 . map show
Upvotes: 1
Reputation: 47042
Here's one way.
import Data.List (cycle)
format :: Show a => [a] -> String
format = concat . zipWith (++) ("" : cycle [" ", " ", " | "]) . map show
This does has the drawback that the grouping into groups of three is hard-coded, but it is not too difficult to generalise.
Upvotes: 3
Reputation: 32455
You could do
threes [] = ""
threes xs = let (front,rest) = splitAt 3 xs in
unwords (map show front) ++
if null rest then "" else " | " ++ threes rest
giving
*Main> threes [1..10]
"1 2 3 | 4 5 6 | 7 8 9 | 10"
Functions I used:
splitAt :: Int -> [a] -> ([a], [a])
-- splitAt 2 "Hello Mum" = ("He","llo Mum")
unwords :: [String] -> String
-- unwords ["Hello","there","everyone"]
-- = "Hello there everyone"
null :: [a] -> Bool
null [] = True
null _ = False
Upvotes: 2