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.
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.
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.
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
The first part is the easiest, you need to convert the numbers to Strings,
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
Related
I'm looking to convert a 2D list to a formatted string.
table :: [[Int]] -> String
Input: [[11,2,30],[4,555,9]]
Output: " 11 2 30 \n 4 555 6 \n"
11 2 30
4 555 9
Basically would need to be able to print out in a grid-shape and maintain formatting to account for the max value.
I know for simple lists I can map show [1,2,3,4] -> ["1","2","3","4"] according to ghci. That seems to keep it as a list though?
Not sure how I can traverse the 2D and keep concatenating as a string and adding \n as needed.
Playing at the REPL can help.
> :t '1'
'1' :: Char
> :t "1"
"1" :: [Char]
> ['1','2']
"12" :: [Char]
> :t concat
concat :: [[a]] -> [a]
> concat ( map show [1,2,3,4] )
.....
> concat [ show x | x <- [1,2,3,4] ]
.....
> concat [ " " ++ ( ensureWidth 9 (show x) ) | x <- [1,2,3,4] ]
.....
> :t replicate
replicate :: Int -> a -> [a]
> replicate 2 '1'
"11" :: [Char]
Now you need to write the function
ensureWidth w s = replicate n ' ' ++ s
but what n to use? You will need to know the length of s, for that. Function length can help.
I want to rotate a string in haskell, so if I give "Now I want to scream" to rotate [[want to scream now I],[scream now I want to]] , if the string start with "I" or "to" then must eliminate it. Till now I still have problems with the rotation.
reverseWords :: String -> String
reverseWords = unwords . reverse . words
shiftt :: [a] -> Int -> [a]
shiftt l n = drop n l ++ take n l
rot::String->[String]
rot l = [ reverseWords l i | i <- [0 .. (length l) -1]]
create a list of all rotations, then filter out based on your predicate. For example,
rotations x = take (length x) $ iterate rot1 x
where rot1 = drop 1 x ++ take 1 x
filteredRots = map unwords . filter (\x -> length (head x) > 2) . rotations . words
and use as
> filteredRots "Now I want to scream"
["Now I want to scream","want to scream Now I","scream Now I want to"]
Prelude>
New to Haskell:
Hi can't seem to figure this out.
What I am trying to do is take a string, turn it in to a [Int] (with map ord)
Change some numbers that fulfils something (in this case x mod 3 == 0).
afterwards I'd like to turn the unchanged numbers back to char, and changed numbers still numbers. Combine this into a string again..
This is my problem:
*Main> fromStringToList "hihello"
[104,105,104,101,108,108,111]
*Main> changeB3 [104,105,104,101,108,108,111]
"'h'210'h''e'216216222"
What I want is:
"h210he216216222"
I'm stuck figuring out how to use show and map to get this to work without the '_' from Char. Thanks.
My Code:
import Data.Char
fromStringToList :: String -> [Int]
fromStringToList "" = []
fromStringToList myString = map ord myString
{-
changeB3
PRE: True
POST: every Int that can be divided by 3 is multiplied by 2 and
kept as int, otherwise transformed to char
-}
changeB3 :: [Int] -> String
changeB3 [] = ""
changeB3 (x:xs)
| x `mod ` 3 == 0 = show map (x * 2 ) ++ changeB3 xs
|otherwise = map chr x ++ changeB3 xs
I will comment your code.
fromStringToList :: String -> [Int]
fromStringToList "" = []
fromStringToList myString = map ord myString
The second line is redundant: when myString is empty, map returns [] anyway. You should remove it.
changeB3 :: [Int] -> String
changeB3 [] = ""
changeB3 (x:xs)
| x `mod ` 3 == 0 = show map (x * 2 ) ++ changeB3 xs
|otherwise = map chr x ++ changeB3 xs
You seem to be confused here. You use a recursive function, but want to use map. You use either recursion or map here, not both.
Assuming you want to use map, you should start by defining how to handle a single Int.
changeB3Single :: Int -> String
changeB3Single x | x `mod` 3 == 0 = ...
| otherwise = ...
Then you map that over the whole list. A first attempt might be
changeB3 :: [Int] -> String
changeB3 xs = map changeB3Single xs -- type error!
but this won't work, since map here returns a list of strings, rather than a single string. We just need to concatenate them.
changeB3 xs = concat (map changeB3Single xs)
Indeed, concat (map ...) is so commonly found that it has its own function in the libraries:
changeB3 xs = concatMap changeB3Single xs
(One could make that pointfree, but there's no need to -- especially for a beginner.)
I have a string "AB0123456789" and the output I would like to have is: "AB01 2345 6789" ... I want to add a space after every fourth character. How can I do this?
Main> addSpace "AB0123456789"
"AB01 2345 6789"
With Data.List.intercalate and Data.List.Split.chunksOf this is easy:
import Data.List.Split
addSpace :: String -> String
addSpace = intercalate " " . chunksOf 4
This may not be the most efficient:
addSpace xs = if length xs <= 4
then xs
else take 4 xs ++ " " ++ addSpace (drop 4 xs)
Demo in ghci:
ghci > addSpace "AB0123456789"
"AB01 2345 6789"
I would think pattern matching would make this easiest:
addSpaces :: String -> String
addSpaces xs#(_:_:_:_:[]) = xs
addSpaces (a:b:c:d:xs) = a:b:c:d:' ':addSpaces xs
addSpaces xs = xs
You have to include the first case so you don't potentially get a space at the end, but it's pretty straightforward. This isn't extensible, though, you wouldn't be able to use a function like this to dynamically choose how many characters you want to skip before inserting a space (such as in #cdk's answer)
You can use splitAt. Heres a function that adds space after every nth character.
spaceN :: Int -> String -> String
spaceN n = init . go
where go [] = []
go xs = let (as, bs) = splitAt n xs in as ++ (' ' : go bs)
for your specific case:
λ. spaceN 4 "AB0123456789"
"AB01 2345 6789"
window :: Int -> [a] -> [[a]]
window i = unfoldr (\l -> if null l then Nothing else Just (splitAt i l))
addSpace :: String -> String
addSpace = intercalate " " . window 4
I need to print out a matrix in haskell so it looks like this:
main> putStr (showMat [[1,-500,-4], [100,15043,6], [5,3,10]])
1 -500 -4
100 15043 6
5 3 10
So far I have come up with this:
type Matrix a = [[a]]
type IntMat = Matrix Integer
showMat :: IntMat -> String
showMat [] = ""
showMat ((y:ys):xs) = (printRow (rowmaxs) (elements) (y:ys)) ++ "\n" ++ showMat xs
where rowmaxs = rowMaxs ((y:ys):xs) ; elements = elementLengths (y:ys)
rowMaxs :: IntMat -> [Int]
rowMaxs [] = []
rowMaxs (x:xs) = [length (show (maximum (x)))] ++ (rowMaxs xs)
elementLengths :: [Integer] -> [Int]
elementLengths [] = []
elementLengths (y:ys) = [length (show y)] ++ (elementLengths ys)
printRow :: [Int] -> [Int] -> [Integer] -> String
printRow [] (a:as) (y:ys) = ""
printRow (z:zs) (a:as) [] = ""
printRow [] [] (y:ys) = ""
printRow [] [] [] = ""
printRow (z:zs) (a:as) (y:ys) = addSpaces (z-a) ++ show y ++ [' '] ++ printRow zs as ys
addSpaces :: Int -> String
addSpaces 0 = ""
addSpaces n = " " ++ addSpaces (n-1)
Which returns this:
Main> putStr (showMat [[1,23,1],[23,56,789],[1234,0,1]])
1 23 1
23 56
1234
I can see the loss of the elements at the end of rows is due to the cases in the printRow function, but I don't know how to fix it. Also the characters don't take into account the ones printed before them. Any help on how I can fix this would be appreciated.
I've written a small script some time ago that takes a tab-separated table (CSV style) and prettyprints it to the console. I've extracted the relevant parts and uploaded the source code here. For example, given your input
m = [[1,23,456],[78,-90,123],[4567,8,9]]
putStrLn $ showMat m will make it into
1 -500 -4
100 15043 6
5 3 10
There's also a function (commented out) at the very bottom if you want left alignment.
In showMat you calculate rowmaxs and elements anew for each row. In particular, rowmaxs has an element for each row left in the matrix, but printRow uses it as meaning something for each column of the matrix.
Edit: Here's a slightly better version of showMat:
showMat :: IntMat -> String
showMat rows = concat (map perRow rows)
where rowmaxs = rowMaxs rows
perRow cols = printRow rowmaxs elements cols ++ "\n"
where elements = elementLengths cols
It's still buggy --- rowMaxs (tries to) calculate the maximum length of the numbers in each row, but you really want the maximum length of the numbers in each column. One consequence of that is that you occasionally pass negative numbers to addSpaces, which doesn't cope terribly well, so here's a version of addSpaces that behaves more gracefully:
addSpaces :: Int -> String
addSpaces n = replicate n ' '
So now we get this:
*Main> putStr (showMat [[1,23,1],[23,56,789],[1234,0,1]])
1 23 1
23 56 789
1234 0 1
Better, but not yet working properly.
I have not fixed all the bugs in your code because I sense you are still learning and need the experience of finding them for yourself.
I advise using map/zipWith/zipWith3 instead of explicitly writing the recursion, as it makes code easier to understand.