How to print data of Map as a table? - haskell

I have a Map (Int, Int) a where a can be any type. Based on the Tuple2 stored in the array as key I wanna create a String table which stores the values of the map.
Because the maximal values of the Tuple2 are known and can be large, my first thought was to create a mutable 2D-Array with a default value. While iterating over the entries of the Map, this array could be filled and then printed out. A pseudo-code example with a Map of Ints:
map = Map (Int, Int) Int
table = Array.ofDim (maxY, maxX) withDefaultValue 0
for ((x,y), int) <- map do
table[y][x] = int
end
rows = table.rows map toString
str = rows map (toString ++ lineEnd)
print str
The only problem is: I don't know how to do this in Haskell. Furthermore I don't know if this is the preferred way one goes in Haskell.
I didn't find good examples how to use 2D-Arrays in Haskell, thus can someone give me one? If there is a better way to do this, maybe with a built-in data type such as Table, Matrix, 2DStringBuilder etc., can someone show me an example how to use them?

table = Array.ofDim (maxY, maxX) withDefaultValue 0
That can be done with
table :: Array (Int,Int) Int
table = array ((0,0),(maxX,maxY))
[((i,j),Map.findWithDefault 0 (i,j) map) | i <- [0 .. maxX], j <- [0 .. maxY]]
Then for tabular output of any two-dimensional array,
rows :: Array (Int,Int) a -> [[a]]
rows arr = [[arr ! (r,c) | c <- [clow .. chigh]] | r <- [rlow .. rhigh]]
where
((rlow,clow),(rhigh,chigh)) = bounds arr
rowStrings :: Show a => Array (Int,Int) a -> [String]
rowStrings arr = [unwords (map show row) | row <- rows arr]
tableString :: Show a => Array (Int,Int) a -> String
tableString arr = unlines (rowStrings arr)
prettyPrint :: Show a => Array (Int,Int) a -> IO ()
prettyPrint arr = putStr (tableString arr)
You can also define some the functions point-free,
prettyPrint = putStr . tableString
tableString = unlines . rowStrings
rowStrings = map (unwords . map show) . rows
rows and table are not comfortably defined point-free, though, so I stop here.

Related

Find the max in a list of lists of type Numbers in Haskell

I'm pretty new to Haskell and am trying to find the max value in a list of lists that contain type Numbers. Numbers is defined as:
data Numbers = StrNumber String | IntNumber Int
deriving (Show, Read, Eq)
I already have a function that finds the max int value in a list of lists of type int.
nested_max :: [[Int]] -> Int
nested_max [] = minBound::Int
nested_max list = maxL (map maxL list)
where
maxL xs = foldr gt (minBound::Int) xs
gt x y = if x < y then y else x
And can use getInt to convert Numbers to ints.
getInt x = read x::Int
I've been trying to map getInt to every value in the list being passed in and then applying nested_max on that list, but keep getting errors.
This is what I've been trying:
getInt x = read x::Int
to_int :: [[Numbers]] -> [[Int]]
to_int list = map (\x-> getInt x) list
An example of what the program should do is...
Input: find_max_number [[StrNumber "9",IntNumber 2,IntNumber 8],[StrNumber "4",IntNumber 5],[IntNumber 6,StrNumber "7"],[],[StrNumber "8"]]
Output: 9
The following is my attempt:
data Numbers
= StrNumber String
| IntNumber Int
nestedMax :: [[Numbers]] -> Int
nestedMax = maximum . map toInt . concat
where toInt (StrNumber x) = read x
toInt (IntNumber x) = x
main = do
print $ nestedMax [[StrNumber "9",IntNumber 2,IntNumber 8],[StrNumber "4",IntNumber 5],[IntNumber 6,StrNumber "7"],[],[StrNumber "8"]] -- 9
I hope the code is straightforward...

Printing a game board using a Data.Map

I have a Data.Map (Int, Int) [String] representing the board of a game (I assume that at a position on the map there can be more than one piece). Let's say that i have the following Map:
fromList [ ( (0,1) , ["piece1", "piece2"] )
, ( (2,2) , ["piece3", "piece4"] ) ]
What I am trying to do is printing the table, in this case a 3 X 3 table, with the elements of the map at the position specified while the rest of the spaces are empty or have an 'x'. I have tried to use foldl and map and combinations of the 2 but I just don't get it how i should do it. I have to mention that i am very new to Haskell. Any help would be great!
Output should be:
x | piece1piece2 | x
x | x | x
x | x | piece3piece4
Here's some code that will get you started. It works by iterating over the (row,column) coords of the board. In the iteration it looks up the string to print for that location, defaulting to "x" on failure. Finally the board is printed one row at a time.
The final format is not exactly what you had in mind but it should get you most of the way there.
import Data.Map as DM
import Data.Maybe
import Data.List.Split
main = do
let pieces = fromList [ ( (0,1) , ["piece1", "piece2"] )
, ( (2,2) , ["piece3", "piece4"] ) ]
-- prepare piece name lists for printing
p2 = fmap concat pieces
height = 3
width = 3
-- iterate over board in row/column order
-- lookup name - use "x" if not found
namesInOrder = [fromMaybe "x" $ DM.lookup (row,col) p2
| row <- [0..height-1]
, col <- [0..width-1]
]
-- split into chunks of one row each
rows = chunksOf width namesInOrder
-- print each row on its own line
mapM print rows
I'm going to start off with the import of Data.Map.Strict. Generally the way to do it is
import qualified Data.Map.Strict as M
import Data.Map.Strict (Map)
You have a Map (Int, Int) String, and you need to print out a square array of strings. It seems that the first thing you need to do is calculate the dimensions of the array. We can do that with an indexed fold. It looks like a good one for the job is
foldlWithKey' :: (a -> k -> b -> a) -> a -> Map k b -> a
Hrmm... Those type variables aren't very informative. Let me relabel them:
foldlWithKey'
:: (acc -> key -> elem -> acc)
-> acc
-> Map key elem
-> acc
This function takes an initial value of an accumulator, and a function indicating how to modify that accumulator for each key and element in the map. Let's make a type representing the dimensions of the board:
data Dim = Dim { rows :: !Int, cols :: !Int }
Now we can write
getDim :: Map (Int, Int) a -> Dim
getDim = M.foldlWithKey' update (Dim 0 0)
where
update (Dim rs cs) (r, c) _
= Dim (max rs (r + 1)) (max cs (c + 1))
For each entry in the Map, we adjust the row and column counts as necessary.
Now that we know the dimensions of the board, let's build a representation of it that will be more suitable for printing. The simplest way to do this is probably to iterate over the row and column numbers, looking up the pairs in the map. Let's start by writing a function to get a single row:
getRow :: Int -> Dim -> Map (Int, Int) a -> [Maybe a]
getRow r (Dim {cols = cs}) m =
[ M.lookup (r, c) | c <- [0 .. cs - 1] ]
Here Nothing means that the key was not in the map, and Just whatever means it was.
Now we can use that to get all the rows:
getRows :: Dim -> Map (Int, Int) a -> [[Maybe a]]
getRows dim#(Dim {rows = rs}) m =
[ getRow r dim m | r <- [0 .. rs - 1] ]
Now we can think about displaying! I'm going to leave this to you, but I suggest you consider using Data.List.intercalate and map to turn each row into a string.

How to write function that combines all the following to reach desired output

type Field = String
type Row = [Field]
type Column = [Field]
type Table = [Row]
print_line :: [Int] -> String
print_line lstcolumnwidths = case lstcolumnwidths of
[] -> "+"
n:ns -> '+' :replicate n '-' ++ print_line ns
This code creates a line eg. with an input [3,6] it will return "+---+------+"
print_field :: Int -> Field -> String
print_field columnwidth fieldcont
|isNum fieldcont = replicate (columnwidth - length fieldcont) ' ' ++ fieldcont
|otherwise = fieldcont ++ replicate (columnwidth - length fieldcont) ' '
isNum :: String -> Bool
isNum s = case s of
[] -> True
x:xs -> isDigit x && isNum xs
This code formats a Field eg input 5 "Amy" returns "Amy " and
for input 5 "78" returns " 78"
print_row :: [Int] -> Row -> String
print_row lstcolumnwidths lstfieldcont = convertString (zipWith print_field
lstcolumnwidths lstfieldcont)
convertString :: [String] -> String
convertString x = case x of
[] -> "|"
x:xs -> '|' : x ++ convertString xs
This code returns a formatted row eg input [5,4] ["Tommy","20" ] returns
"|Tommy| 20|"
print_table :: Table -> [String]
I want to write a function for this that takes a Table and returns a list of Fields.
The input will be
[["first","last","gender","age"],["Patricia","Davies","female","20"],
["Paul","Dowden","male","19"],["John","Doe","male","24"],
["David","Barker","male","21"],["Eve","Lee","female","23"],
["Mary","Hester","female","21"]]
and i want the output to be
["+--------+------+------+---+","|FIRST |LAST |GENDER|AGE","|Paul
|Dowden |male | 19","+--------+------+------+---+","|David |Barker |male | 21"...etc ]
Any tips on how to write a function that combines all the previous to reach this would be helpful.
I also have a function
convert :: String -> String
convert list = case list of
[] -> []
c:cs
|isUpper c -> c: convert cs
|otherwise -> toUpper c : convert cs
Which is used to convert the first field into uppercase.
Well, you have nearly everything in place. All you seem to be missing is, first, a means to calculate the widths of the columns. You can define:
colWidth :: Column -> Int
colWidth fields = maximum (map length fields)
to calculate the width of a single column.
Then, you can use transpose from Data.List to transpose your table from a list of rows into a list of columns and map colWidth over those columns:
colWidths :: Table -> [Int]
colWidths table = map colWidth (transpose table)
Second, you'll need the definition of print_table, which is short because you've already put in all the hard work:
print_table :: Table -> [String]
print_table table =
let wdths = colWidths table -- the column widths
separator = print_line wdths -- the row-by-row separator
heading:body = map (print_row wdths) table -- the rows
heading' = convert heading -- the heading in upper-case
in [separator] ++ intersperse separator (heading':body) ++ [separator]
The only "trick" here is using intersperse (also from Data.List) to stick a copy of the separator between every pair of rows.

How can I get a field from each element of a list of custom data types in Haskell?

First of all, if the title is confusing I apologise - I don't know how to phrase it.
I'm learning Haskell and tackling the Knapsack Problem but having a problem with list comprehension.
data Object = Item { name :: String,
weight:: Double,
profit :: Double,
efficiency :: Double }
deriving (Read, Show)
I have a function that takes a list from a .csv file and calculates efficiency and sorts it:
getItemsAsList
= do
body <- readFile "items.csv"
let ls = split '\n' body
let lc = map (split ',') ls
let itemList = map (loadItem) lc
let sorted = sortItems efficiency itemList
return sorted
Functions used:
loadItem :: [[Char]] -> Object
loadItem (n:ow:op:xs) = Item n w p (p/w)
where
w = read ow :: Double
p = read op :: Double
sortItems :: Ord a => (t -> a) -> [t] -> [t]
sortItems fn [ ] = [ ]
sortItems fn (pivot:rest)
= sortItems fn [x | x <- rest, (fn x) > (fn pivot)]
++ [pivot] ++
sortItems fn [x | x <- rest, (fn x) <= (fn pivot)]
split :: Char -> [Char] -> [[Char]]
split _ [] = []
split delim str = if before == [] then
split delim (drop 1 remainder)
else
before: split delim (drop 1 remainder)
where
(before, remainder) = span (/=delim) str
What I am trying to do is write a function that will go through the list returned by the getItemsAsList function and get the value of the weight field from each element and sum them together. From this I can hopefully implement the greedy solution to the problem, once I understand how to get the elements.
Also, the getItemsAsList function returns IO [Object]
Thanks.
To get the weight from a single Object, you do weight obj. Thus, to get the weight from each element of a list of Objects, you do map weight objlist or [weight obj | obj <- objlist]. Also, the Prelude has a sum function which works exactly as you'd expect. Put them all together, and you're done.
You are treating the result of getItemsAsList, which is a monadic function, as a normal value instead of as an IO action.
The concept of a monad is usually explained as it being a box, which you can "unpack" the value from (using the <- operator). When you call it from a pure function, you cannot unpack the value, and instead are just left with the box. (that is what the IO [Object] is, it is an IO box containing an Object list value). You can however, freely use pure functions from inside a monad.
The solution is to call and unpack the value of getItemsAsList from within a monad, and then pass it onto your other pure functions to carry out whatever the rest of your task is.
Once you have unpacked the list of objects from getItemsAsList using the <- operator, you can pass it into other pure functions.

Haskell: how to read values from stdin line-by-line and add them to a map?

I want to read strings from stdin and store them into a map, where key is the input string and value is the number of previous occurrences of this string. In Java I would have done something like this:
for (int i = 0; i < numberOfLines; i++) {
input = scanner.nextLine();
if (!map.containsKey(input)) {
map.put(input, 0);
System.out.println(input);
} else {
int num = map.get(input) + 1;
map.remove(input);
map.put(input, num);
System.out.println(input.concat(String.valueOf(num));
}
}
I've tried doing the same in Haskell by using forM_ but had no luck.
import Control.Monad
import qualified Data.Map as Map
import Data.Maybe
main = do
input <- getLine
let n = read input :: Int
let dataset = Map.empty
forM_ [1..n] (\i -> do
input <- getLine
let a = Map.lookup input dataset
let dataset' =
if isNothing a then
Map.insert input 0 dataset
else
Map.insert input num (Map.delete input dataset)
where num = ((read (fromJust a) :: Int) + 1)
let dataset = dataset'
let output = if isNothing a then
input
else
input ++ fromJust a
putStrLn output)
The contents of dataset in the above code does not change at all.
The Map defined in Data.Map is an immutable data type. Calling Map.insert returns a modified Map, it does not change the one you already have. What you want to do is iteratively apply updates in a loop. Something more like
import qualified Data.Map as M
import Data.Map (Map)
-- Adds one to an existing value, or sets it to 0 if it isn't present
updateMap :: Map String Int -> String -> Map String Int
updateMap dataset str = M.insertWith updater str 0 dataset
where
updater _ 0 = 1
updater _ old = old + 1
-- Loops n times, returning the final data set when n == 0
loop :: Int -> Map String Int -> IO (Map String Int)
loop 0 dataset = return dataset
loop n dataset = do
str <- getLine
let newSet = updateMap dataset str
loop (n - 1) newSet -- recursively pass in the new map
main :: IO ()
main = do
n <- fmap read getLine :: IO Int -- Combine operations into one
dataset <- loop n M.empty -- Start with an empty map
print dataset
Notice how this is actually less code (it's be even shorter if you just counted the number of occurrences, then updateMap dataset str = M.insertWith (+) str 1 dataset), and it separates the pure code from the impure.
In this case, you don't actually want to use forM_, because each step of the computation depends on the previous. It's preferred to write a recursive function that exits at a condition. If you so desired, you could also write loop as
loop :: Int -> IO (Map String Int)
loop n = go n M.empty
where
go 0 dataset = return dataset
go n dataset = getLine >>= go (n - 1) . updateMap dataset
Here I've compressed the body of the old loop into a single line and then put it inside go, this allows you to call it as
main :: IO ()
main = do
n <- fmap read getLine :: IO Int
dataset <- loop n
print dataset
This removes the need to know that you must pass in M.empty into loop for the first call, unless you have a use case to call loop multiple times on the same map.
Your problem is that Map.insert does not do what map.remove does in C++. Map.insert returns a new Map which has the element in it but you are simply throwing this new Map away. This is how nearly all Haskell data structures work, for instance the code:
main = do
let x = []
y = 5 : x
print x
prints the empty list []. The cons : operator does not destructively modify the empty list but returns a new list containing 5. Map.insert does the same but with Maps instead of lists.
First regarding your java code, you do not need to remove from the map before inserting a new value.
Regarding haskell, the language does not work the way you think it does : your let trick is not updating a value, everything is basically immutable in haskell.
Using only the basic getLine, one way to do it is to use recursion:
import qualified Data.Map as Map
type Dict = Map.Map String Int
makeDict ::Dict -> Int -> IO Dict
makeDict d remain = if remain == 0 then return d else do
l <- getLine
let newd = Map.insertWith (+) l 1 d
makeDict newd (remain - 1)
newDict count = makeDict Map.empty count

Resources