How do I create a printRow function in Haskell? - haskell

I have the following function in Haskell:
printRow :: [(Int, String)] → String
I want it, given a list of pairs (the left element
giving the desired length of a field and the right element its contents), to format one row in a table. For example,
printRow [ (5, "Alice"),(6, "Allen"),(6, "female"),(6, "82000")]
should return the formatted row
"|Alice|Allen |female| 82000|"
I know that I should probably use the functions intercalate, map and uncurry, but I'm a bit stuck, as I'm not very familiar with functional programming. I've tried something in the likes of:
printRow (int, string) = if all isDigit string
-- all_digits should be right-aligned
then ...
else map intercalate "|" uncurry (int, string)
As you can probably guess, this won't work, and I'm not sure how to do it.

Knowing how to handle one formatting specification,
printOne :: (Int, String) -> String
printOne (int, string) =
if all isDigit string
then a ++ b ++ "|"
else b ++ a ++ "|"
where
a = ....
b = ....
c = length string
we can turn each specification in a list into a formatted string, with map, and concatenate the results
printRow specs = a ++ concat ( map printOne specs )
where
a = ....
You should be able to complete the code. To repeatedly output a space character, use replicate :: Int -> a -> [a].

Related

Haskell: Convert String to [(String,Double)]

I parse an XML and get an String like this:
"resourceA,3-resourceB,1-,...,resourceN,x"
I want to map that String into a list of tuples (String,Double), like this:
[(resourceA,3),(resourceB,1),...,(resourceN,x)]
How is it possible to do this? I ve looked into the map function and also the split one. I am able to split the string by "-" but anything else...
This is the code i have so far:
split :: Eq a => a -> [a] -> [[a]]
split d [] = []
split d s = x : split d (drop 1 y) where (x,y) = span (/= d) s
it is just a function to split my string into a list of Stirng, but then i dont know how to continue.
What I want to do know is to loop over that new list that i have created with the split method and for each element create a tuple. I hace tried with the map function but i dont get it to compile even
So in Haskell you dont really mutate any value, instead you'll create a new list of pairs from the string you've described, so the solution would look something similar to the following:
import Data.List.Split
xmlList = splitOn "-" "resourceA,3-resourceB,4-resourceC,6"
commaSplit :: String -> [String]
commaSplit = splitOn ","
xmlPair :: [String] -> [(String, Double)] -- might be more efficient to use Text instead of String
xmlPair [x] = [(\x' -> ((head x') :: String, (read (last x')) :: Double )) (commaSplit x)]
xmlPair (x:xs) = xmlPair [x] ++ xmlPair xs
main :: IO ()
main = mapM_ (\(a,b) -> putStrLn (show a++" = "++ show b)) (xmlPair $ xmlList)
This is my quick and dirty way of showing things but I'm sure someone can always add a more detailed answer.

Is there a way to map a function that will read a list of strings and concatenate them into one big string?

Can I get a quick explanation on how I would concatenate a list of strings into one string using map? I was trying to use intercalate but I realized that this is used to combine lists and not strings. i.e. [[char]] instead of [String]
type InformationList = (String , [String] )
concatenateList :: String -> [InformationList] -> String
concatenateList n cs = do
let [informationlist] = intercalate " " cs
let toWrite = n ++ [informationList]
return toWrite
I updated this so that you can see the code I've been working with. It is saying that [[char]] is not the same as [informationlist] but that should be the same as [String]
A String is a list of Chars. A String is a type alias defined as:
type String = [Char]
If you want to concatenate a list of Strings without any separators, you can use concat :: Foldable t => t [a] -> [a]:
Prelude> concat ["foo", "bar", "qux"]
"foobarqux"
In case you want to insert a string as delimiter, you can use intercalate :: [a] -> [[a]] -> [a]:
Prelude> import Data.List(intercalate)
Prelude Data.List> intercalate "," ["foo","bar","qux"]
"foo,bar,qux"

Haskell expand function

I was recently handed an assignment I have almost completed and I am currently in need of some help.
The first functions I needed to implement were lookUp, split, combine and keyWordDefs.
I then had to implement a function expand :: FileContents -> FileContents -> FileContents that takes the contents of a text file and an info file and combines them using the above functions to build a string representing the output file.
Here is my code so far:
module MP where
import System.Environment
type FileContents = String
type Keyword = String
type KeywordValue = String
type KeywordDefs = [(Keyword, KeywordValue)]
separators :: String
separators
= " \n\t.,:;!\"\'()<>/\\"
lookUp :: String -> [(String, a)] -> [a]
-- Given a search string and a list of string/item pairs, returns
-- the list of items whose associated string matches the search string.
lookUp x y = [a|(b,a) <- y, x==b]
split :: String -> String -> (String, [String])
-- Breaks up a string.
split as [] = ("",[""])
split as (b:bs)
| elem b as = (b:xs,"":y:ys)
| otherwise = (xs, (b:y):ys)
where
(xs,y:ys) = split as bs
combine :: [Char] -> [String] -> [String]
-- Combines the components of a string from its constituent separator
-- characters and words, as generated by a call to split.
combine [] y = y
combine (x:xs)(y:ys) = y : [x] : combine xs ys
getKeywordDefs :: [String] -> KeywordDefs
-- Takes the contents of an information file in the form of a list
-- of lines and which returns a list of keyword/definition pairs.
getKeywordDefs [] = []
getKeywordDefs (x:xs) = (keyword, concat defs) : getKeywordDefs xs
where
(_, (keyword : def)) = split " " x
defs = combine spaces def
spaces = [ ' ' | s <- [2..length def]]
expand :: FileContents -> FileContents -> FileContents
An example of the function expand is this:
expand "The capital of $1 is $2" "$1 Peru\n$2 Lima."
"The capital of Peru is Lima."
I suppose that this is going to work by 1st looking up (with function lookUp) if there is a "$" in the input string, then split the words, then replacing words that begin with "$" with the second input string, then combining them again all together? I am really confused actually, and I would like to know if anyone here understand how function expand will work.
Any help is welcome :)
Your expand function should look something like this:
-- It's probably better to change the type signature a little bit
-- because we're not returning the contents of a file, we're returning a string.
expand :: FileContents -> FileContents -> String
expand fc1 fc2 = let
keywordDefs = getKeywordDefs fc2
in replaceSymbols fc1 keywordDefs
Then you need a function named replaceSymbols, which splits up fc1 whenever it sees a $X, and then substitutes that $X for the result of looking up $X in keywordDefs.
replaceSymbols :: FileContents -> KeywordDefs -> String
Have a go at implementing that function and reply to this answer if you still need help :).

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.

What is the inverse of this function?

flattern :: [(Char, Int)] -> String
flattern [] = ""
flattern ((w,l):xs) = show l ++ w : flattern xs
What would be the inverse function of this? Is there any way of be able to work this out?
It's not invertible:
There are strings that cannot be reproduced by this function (any string not starting with a digit).
It's not even partially invertible. There are also strings that correspond to multiple inputs: "1111" can be produced by either [('1',1),('1',1)] or [('1',111)].
Are you sure that this was the function to invert, and not something like flattern ((w,l):xs) = replicate l w ++ flattern xs?
If you really want, you could parse the output of the function to try and reconstruct what the arguments must have been.
import Text.ParserCombinators.Parsec
unflat1 :: Parser (Char, Int)
unflat1 = do
c <- anyChar
n <- many1 digit
return (c, read n)
readExpr :: String -> Either String [(Char, Int)]
readExpr input = case parse (many unflat1) "unflat" input of
Left err -> Left ("No match: " ++ show err)
Right val -> Right val
This sorta works, as long as flattern does not have numbers as the first input.
Something like flattern [('a',1), ('2',3)] will be parsed as [(a, 123)].

Resources