Haskell: Printing out the contents of a list of tuples - haskell

Basically what I need to do is write a function that takes in a list of type [(String, String)] and prints out the contents so that, line-by-line, the output looks like this:
FirstString : SecondString
FirstString : SecondString
..etc, for every item in the list. I've got the following code and it prints it out, but for some reason it prints out a line containing [(),()] at the end.
display :: Table -> IO ()
display zs = do {
xs <- sequence [putStrLn ( a ++ " = " ++ b) | (a, b) <- zs];
print xs
}
Is there anything I'm doing wrong?

The final print xs is unnecessary. sequence here is returning a bunch of ()s (the return value of putStrLn), and print is printing that out as well.
While you're at it, now that print xs is gone, you can get rid of the xs variable binding, and make sequence into sequence_ to throw away the return value, giving:
display :: Table -> IO()
display zs = sequence_ [putStrLn (a++" = "++b) | (a,b) <- zs]

You could even use mapM:
display :: Table -> IO ()
display = mapM_ (\(a,b) -> putStrLn (a++" = "++b))

I would agree with ja that you should split up your code in to two functions:
A pure part: a function that takes your data structure and turns it into a string
An impure part, that renders that string to the console
Here's a simple implementation:
showTable :: Table -> String
showTable xs = concatMap format xs
where
format (a, b) = a ++ " : " ++ b ++ "\n"
display :: Table -> IO ()
display table = putStr (showTable table)
This design has two advantages:
For one, most of your `logic' is in the pure part of the code, which is nice, in a functional programming kind of way.
Secondly, and this is just simple software engineering principle; you now have a reusable function that you can use, should you ever want to format your data structure in another part of your code (seems likely).

Write a function that takes a tuple to a string, formatted as you wish.
Then concatMap that function over your list; print the result.

Related

Reading numbers from input Haskell

I want to have a function that reads arbitrary int's until the number '0' is inserted, and then presents the numbers inserted in an ordered list.
For that i wrote this function:
import Data.List
readIntegers :: IO()
readIntegers = do
putStrLn "insert a number: "
num<-getLine
let list = ordList ((read num :: Int):list)
if (read num == 0)
then print list
else readIntegers
where ordList ::[Int]->[Int]
ordList [] = []
ordList xs = sort xs
This compiles just fine, but when i insert the number '0', it gives me this error:
*** Exception: <<loop>>
What am i doing wrong ?
As #phg points out, you are essentially constructing an infinite list, and actually evaluating it causes the loop error. A simple implementation to resolve this issue is to define a helper function which takes an additional parameter - a list to store all the inputs read in from the screen, like so:
readInteger :: IO ()
readInteger = readInteger' []
where
readInteger' x = do
putStrLn "insert a number: "
num<-getLine
if ((read num :: Int) == 0)
then print $ ordList x
else readInteger' $ (read num :: Int):x
where ordList ::[Int]->[Int]
ordList [] = []
ordList xs = sort xs
Please note that the above is essentially just an implementation of #phg's answer, but with some changes to your original logic. Firstly, since 0 is a sentinel value, we shouldn't be appending that to our list. Second, we do not need to sort the list every single time we are adding a value to it. Sorting once at the time of printing/passing to another function is sufficient.
Demo
If you want to read an unspecified number of integers without prompting for user input and cut it off the moment you encounter 0, you would probably do well to use getContents, which will read everything from the standard input as a single string, lazily.
Then, it is a simple matter of parsing it to a list of numbers and doing what you want with it, like so:
readIntegers :: ()
readIntegers = do
a <- getContents
let b = ordList $ takeWhile (/= 0) $ map (\x -> read x :: Int) $ words a
mapM (putStrLn . show) b
where ordList ::[Int]->[Int]
ordList [] = []
ordList xs = sort xs
let list = ordList ((read num :: Int):list)
This is basically a recursive definition of a list of the form [x, x, ...] (like if you wrote an equation saying x = 1 + x). That is perfectly fine by itself, since Haskell is lazy; however, if you try to print list (aka "solve the equation"), it will fail, since it will try to print infinitely many numbers.
You probably have a misconception about the workings of the (:) operator. Haskell functions will never perform an assignment operation and concatenate num onto list by changing it, like in imperative languages. There are only pure functions.
If you want to accumulate all numbers, you should try to come up with a recursive definition of readIntegers, keeping its state (the list) in an additional parameter (there are also more sophisticated ways, hiding the state passing, but more complicated to use for a beginner).
For a more sophisticated solution, note that this is an unfold and you can use unfoldM from Control.Monad.Loops to implement it:
import Control.Monad.Loops (unfoldM)
readInts :: IO [Int]
readInts = unfoldM $ fmap (check . read) getLine
where check x = if x == 0 then Nothing else Just x
This has the nice property that it returns the list in the order in which it was read.

Printing a list in Haskell

I'm sure these are both very stupid mistakes, but i'm trying to convert and print two lists , and i'm getting an error on ghci.
First, i want to convert from this:
["2","2","2"]
to this
[2,2,2]
for that, i wrote this function:
convert (x:xs) = [read x | x <- xs]
but that doesn't seem to be working...
Second:
Here's my printing function:
print_results [] _ = error("Empty List!")
print_results _ [] = error("Empty List!")
print_results (x:xs) (y:ys) = print x ++ " + " ++ print y : print_results xs ys
For this input:
[2,2,2] and [3,3,3]
The desired output should be:
2 + 3
2 + 3
2 + 3
Thanks in advance!
These are not "stupid" mistakes, but you're going to have to step back a bit from "what type do I write here" in order to make sense of what's going on. I notice you've asked a bunch of overlapping questions today surrounding these issues. I hope we as a community can get you an answer that will get you on the right track. In that light, I'm marking this post Community Wiki and encouraging others to edit it.
In Haskell, every value has a specific, concrete type. We can write functions that work on multiple types. Some of them work on all types: replicate 5 :: a -> [a] doesn't care at all about what a is. Some work only on some types: read :: Read a => String -> a requires that a be an instance of the class Read.
For now, you should assume that, in order to actually run a function and print a result in GHCi or compiled code, you need to replace all type variables to specific types. (This is wrong in lots of ways that I or others will probably expand on.)
After writing a function, ask GHCi for its inferred type, which is usually the most general signature possible:
> :t map read
map read :: Read a -> [String] -> [a]
> :t map read $ ["as","ew"]
> map read $ ["as","ew"] :: Read a => [a]
Notice that we still have a type variable in there. We need to choose a specific type. What #chi and I both encouraged you to do was to add a type annotation somewhere to fix that type. But if you fix that type to Int, you're trying to parse "as" and "ew" as numbers, which obviously fails.
First:
convert :: [String] -> [Int]
convert xs = [read x | x <- xs]
or even
convert :: [String] -> [Int]
convert = map read
Note that the type annotation matters, since without it Haskell does not how how it should read the string (as an Int? a Char? a tree? a list? etc.)
Second:
print_results [] [] = []
print_results [] _ = error "Empty List!"
print_results _ [] = error "Empty List!"
print_results (x:xs) (y:ys) = (show x ++ " + " ++ show y) : print_results xs ys
The above will compute a list of strings, formatted as you wanted. If you really want to print them doing an IO action, you can use
mapM_ putStrLn (print_results list1 list2)

Loading a text file of values into a list of lists

I have a text file (data.txt) that looks like this:
--#--
--#-#
-----
This is a MAP where (-) is places I can go, (#) is a wall, and (#) is my goal. I want to load this into a list of lists so that I can index it via (row,col) and then traverse it (unless there is a better way, I am a newb here).
I am using readFile but this gives it to me all in one shot, trying to use .lines to break it up but that still won't work ...
I was hoping to end up with something like this:
myMap = [[-,-,#,-,-],[-,-,#,-,#],[-,-,-,-,-]]
Unless I am mistaken this would let me do stuff like myMap !! 0 !! 0 to get [-] which is the CHAR at cell (0,0) on my map (and so forth).
Then with this I could pass it into a function and determine what moves I can do (not move into a wall obviously) and try to find some way to recursively move until I get to my (#), etc...
If I understood correctly, you want to load a string into a two-dimensional map. You can do something like:
import Data.Map hiding (map)
toMap :: String -> Map (Int,Int) Char
toMap = fromList . concatMap (λ(r,line) -> map (λ(c,char) -> ((r,c),char)) $ zip [0..] $ line) . zip [0..] . lines
and then use it:
raw :: String
raw = "--#--λn--#-#λn-----"
main :: IO ()
main = do
let myMap = toMap raw
putStrLn $ "Map is: " ++ show myMap
putStrLn $ "Elem in pos (0,0): " ++ show (myMap ! (0,0))

Create List of Strings in Haskell

I am making the pilgrimage from Java to Haskell. Broadly speaking, I get the main concepts behind Haskell. Reading all the tutorials and books 'makes sense' but I am getting stuck writing my own code from scratch.
I want to create 1000 files on the file system with names
"myfile_1.txt" ... "myfile_1000.txt"
and each containing some dummy text.
so far I have worked out the whole IO thing, and realise I need to build a list of Strings 1000 elements long. So I have:
buildNamesList :: [] -> []
buildNamesList ???
Once I have the List I can call the writefile method on each element. What I can't figure out is how to add a number to the end of a String to get each fileName because I can't have an int i = 0, i ++ construct in Haskell.
I am a bit out of my depth here, would appreciate some guidance, thanks
One possible solution:
buildNamesList = map buildName [1..1000]
where buildName n = "myfile_" ++ show n ++ ".txt"
import Control.Applicative
fileNames = ("myFile_"++) <$> (++".txt") <$> show <$> [1..1000]
how do I then traverse over it, pluck out the String at element n and then pass it into another function?
No! "Plucking out" something from a list in inefficient. You don't want to worry about how to get to each element, then do something with it. That's necessary in imperative languages because they don't have a proper abstraction over what "sequencing actions" means – it's just something magical built into the language. Haskell has much more well-specified, mathematically sound and type-safe magic for that; as a result you don't need loops and suchlike.
You know what to do with each element (String -> IO ()), and you know where the data comes from ([String]). You also know what should eventually happen (IO ()). So the combinator you're looking for has type ( String -> IO() ) -> [String] -> IO (), though obviously it doesn't really depend on the data being Strings, so let's simplify that to (a -> IO()) -> [a] -> IO(). You can look that up on Hoogle, which offers amongst sume rubbish mapM_ and forM_, both of which do what you want:
mapM_ (\filename -> writeFile filename "bla") filenamesList
or
forM_ filenamesList $ \filename ->
writeFile filename "bla"
Sometimes I think of foldr as bearing some resemblance to a for loop. Here's something kind of like the i++ construct, applying i inside a loop:
foldr (\i accum -> ("myfile_" ++ show i ++ ".txt") : accum) [] [1..1000]
Another way could be zipWith, which applies a function to combine two lists:
zipWith (\a b -> a ++ show b ++ ".txt") (repeat "myfile_") [1..1000]
or
zipWith ($) (repeat (("myfile_" ++) . (++ ".txt") . show)) [1..1000]
And here's a recursive example, too, applied as fileList "myfile_" ".txt" [1..1000]:
fileList _ _ [] = []
fileList fName ext (x:xs) = (fName ++ show x ++ ext) : fileList fName ext xs

Grab a string from a list and save it into another list?

I'm trying to grab a random item from a string list and save that into another string list but I can't get my code to work.
import System.Random
import Control.Applicative ( (<$>) )
food = ["meatballs and potoes","veggisoup","lasagna","pasta bolognese","steak and fries","salad","roasted chicken"]
randomFood xs = do
if (length xs - 1 ) > 0 then
[list] <- (fmap (xs!!) $ randomRIO (0, length xs -1))
else
putStrLn (show([list])
I'm getting parse error on input '<-' but I'm sure there are more issues then that. There is also the issue that the list may contain the same dishes two days in a row which is not what I want and I guess I can remove duplicates but that also would remove the number of items in the list which I want to stay the same as the number in the list.
Anyone have a good idea how I could solve this? I have been searching for a day now and I can't find something useful for me but that's just because I'm looking in the wrong places. Any suggestion on how I can do this or where I can find the info will be greatly appreciated!
The reason it didn't work is that you needed another do after your if...then. (After a then you need an expression, not a pattern <- expression.)
randomFood :: String -> IO () -- type signature: take a String and do some IO.
randomFood xs = do
if length xs > 1 then do
[list] <- (fmap (xs!!) $ randomRIO (0, length xs -1))
else
putStrLn (show([list])
But that still doesn't compile, because you don't actually do anything with your list.
At the end of every do block, you need an expression to return.
I think you meant to still print some stuff if the length of xs is too short, and you probably meant to print the selected food if there was more than one to choose from.
Better would be:
randomFood :: String -> IO ()
randomFood xs | length xs <= 1 = putStrLn $ show xs
randomFood xs | otherwise = do
item <- (xs!!) <$> randomRIO (0, length xs -1)
putStrLn $ show(item)
This | boolean test = syntax is better for conditional answers based on input.
I changed [list] to item because you're selecting a single item randomly, not a list of items.
Haskell is quite happy to let you put [list], because any string that's got one character in it matches [list].
For example, "h" = [list] if list='h', because "h" is short for ['h']. Any longer string will give you Pattern match failure. In particular, all the food you've specified has more than one character, so with this definition randomFood would never work! item will match anything returned by your randomRIO expression, so that's fine.
You imported <$> then didn't use it, but it's a nice operator, so I've replaced fmap f iothing with f <$> iothing.
I finally realised I'm doing the wrong thing with short lists; if I do randomFood ["lump of cheese"] I'll get ["lump of cheese"], which is inconsistent with randomFood ["lump of cheese"] which will give me "lump of cheese".
I think we should separate the short list from the empty list, which enables us to do more pattern matching and less boolean stuff:
randomFood :: String -> IO ()
randomFood [] = putStrLn "--No food listed, sorry.--"
randomFood [oneitem] = putStrLn . show $ oneitem
randomFood xs = do
item <- (xs!!) <$> randomRIO (0, length xs -1)
putStrLn . show $ item
This gives three different definitions for randomFood depending on what the input looks like.
Here I've also replaced putStrLn (show (item)) with putStrLn . show $ item - compose the functions show and putStrLn and apply ($) that to the item.
Few points to note :
Don't intermix pure and impure code.
Try to use library for a task rather than repeating what is already written.
Here is the code using random-fu library
import Data.Random
import Control.Applicative
food :: [String]
food = ["meatballs and potoes","veggisoup","lasagna","pasta bolognese","steak and fries","salad","roasted chicken"]
randomFood :: [String] -> RVar (Maybe String)
randomFood [] = return Nothing
randomFood xs = Just <$> randomElement xs
main :: IO ()
main = (sample $ randomFood food) >>= print
This is like choosing one element from a list randomly.
> main
Just "steak and fries"
> main
Just "meatballs and potoes"
If you want to output just a random permutation of the above list, you can use shuffle like
main = (sample $ shuffle food) >>= print
Example
> main
["meatballs and potoes","lasagna","steak and fries","roasted chicken","salad","pasta bolognese","veggisoup"]
> main
["roasted chicken","veggisoup","pasta bolognese","lasagna","steak and fries","meatballs and potoes","salad"]

Resources