Haskell Ending Loop Return - haskell

I am trying to stop my program from stopping after the loop in a function ends. Let me first give some background - After a recent failed attempt at Haskell (Here) i have been working through a number of tutorials and have been able to successfully create myself lists containing both a name and priority (Below, working Code - Apologies if the copy/paste has miss-indented it).
module Main where
-- Function Main, gather details, then print
main :: IO ()
main = do info <- gatherInfo
let names = info
putStrLn . unlines . map printDetails $ names
-- Function to Get the Info
gatherInfo :: IO [(String, Int)]
gatherInfo = do putStr "Name: "
name <- getLine
if name == "quit"
then return []
else do priority <- getPri
let info = (name,priority)
otherInfo <- gatherInfo
return $ info : otherInfo
-- Function to get Priority
getPri :: IO Int
getPri = do putStr "Priority: "
input <- getLine
let parsed = reads input
if parsed == []
then do putStrLn "Incorrect Entry, please enter again"
getPri
else return $ fst $ parsed !! 0
--Function to print each detail
printDetails :: (String, Int) -> String
printDetails (name, priorityScore) = "Name: " ++ name ++ "Priority: " ++ show priorityScore
This code will print the following output(incl user input):
Patient Name: test1
Patient Priority: 1
Patient Name: test2
Patient Priority: 2
Patient Name: quit
Patient name: test1 Priority: 1
Patient name: test2 Priority: 2
As you can see, the list items are entered and then upon entering 'quit' the code prints the list contents.
The problem i have is that upon entering 'quit' and printing the list items, when i enter back into the program the items have gone.
What i am trying to achieve is running the gatherInfo function, then printing the list (in its entirety) and then going back to the gatherInfo function to add another item. For example, my output may be like:
Patient Name: test1
Patient Priority: 1
Patient name: test1 Priority: 1
Patient Name: test2
Patient Priority: 2
Patient name: test1 Priority: 1
Patient name: test2 Priority: 2
Patient Name: quit (Quit program, doesn't matter what happens now)
I have made several attempts at implementing this; for example, calling my printDetails function during each gatherInfo loop to print 'info', but with little success. How can i implement this into my design?

I suggest you do this:
Modify your gather info function so it takes info of only one patient.
Then create another function, let's call it main':
main' patients = do patient <- gatherInfo
if (fst patient == "quit")
then return ()
else do
let patients' = patient:patients -- add patient to list of patients
putStrLn . unlines . map printDetails $ patients' -- print list of patients
main' patients' -- continue
What are we doing here is remembering state of your program by passing parameter (this is how it is done in Haskell, as you have no states).
Now your main function would look like this:
main = main' [] -- start with empty list of patients
You may notice that I presumed your function gatherInfo will return patient with name "quit" when user inputs "quit", however you can do that some other way

Related

How can I get an modulo of a calculation in Haskell?

I am simply trying to get the Unixtime of the current day. For that I wanted to get the modulo of the current time divided by the timespan of one day. .
yourUnixTime = k / 86400
modunix = mod yourUnixTime 1
main = do
putStrLn "give me your time"
k <- readLn
putStrLn $ "time: " ++show (modunix yourUnixTime)
This is my current code. So why does my code not work?
It says "Variable not in scope: k" I also tried to put a "k" between the "yourUnixTime" and the equal sign, didnt work either (with even more errors I can't understand). So the answer I am hoping for is how I can fix it and/or what other solutions are there.
You are trying to use the variable k before it has been defined or initialized.
Here is the modified version of the code
main = do
putStrLn "Enter the current time:"
k <- readLn
let (unixTime, modUnix) = k `divMod` 86400
putStrLn $ "Unix time of the current day: " ++ show modUnix

How to print output on a single line?

Today I was working on this code which outputs the average value of a series of "arrays", the data is inputted in this format:
3 #Number of arrays to get the average from
2 3 0 #First array
4 5 0 #Second array
1 4 5 0 #Third array
I worked on a code that outputs the data, but realized that it prints it like this:
2 #Average (Int) of the 1st array
4 #Average (Int) of the 2nd array
3 #Average (Int) of the 3rd array
(Take into account that the 0 at the end of every array is not used when calculated the average, it only exists for means of indicating end of array)
My question is, How can I properly change my code so that I can output the data like this? :
2 4 3
Here is the code I've been working in:
sumList :: [Int] -> Int sumList [] = 0 sumList (u:v) = u + sumList v
funavg :: Int -> IO () funavg numitint = if numitint==0 then return ()
else do
arrs <- getLine
let arrnum = (map read(words arrs) :: [Int])
let total = sumList arrnum
let avg = div total ((length arrnum)-1)
print avg
funavg (numitint - 1)
main :: Prelude.IO () main = do
numits <- getLine
let numitint = read numits :: Int
funavg numitint
I've searched many documents and websites, but can't come with an ideal answer.
Using recursion is mandatory.
Any help is highly appreciated :D
print is equivalent to putStrLn . show and is provided for convenience to print a single value of any Show type.
print does not have a standard library companion which omits the newline, but putStrLn does: it's called putStr. Instead of print avg, consider
putStr $ show avg

How to change an element in [String] in Haskell?

I'm working on a program that receives as input a board game as follows:
#####
#_ ##
# ##
# #
# .#
#####
1 4 (player initial position, marked with '_')
After receiving the input, the program transforms it to a [String].
This case, it would be:
["#####", "#_ ##", "# ##", "# #", "# .#", "#####", "1 4"]
How can I access position [1,4] and transform '_' to 'o'?
Function must return initial list with that transformation.
Very important note: '_' is never displayed on input, I only used it to make clear where position [1,4] is (therefore, on input we only see a blank space, ' ')
Seems like one of those tasks you might have to solve for online coding games. As others pointed out, lists are not really suited for dealing with coordinates like this. However, if you are not able to use better libraries (like in coding games) you will have to do some more work.
Here is the code from my ghci session (transforming to a proper program is left as an exercise for the reader...):
let input = ["#####", "#_ ##", "# ##", "# #", "# .#", "#####", "1 4"]
let reverseInput = reverse input
let position = head reverseInput
let board = tail reverseInput
let posX = read $ takeWhile (/=' ') position :: Int
let posY = read $ takeWhile (/=' ') $ reverse position :: Int
let (unchangedBoard, changedBoard) = splitAt posY board
let (unchangedRow, changedRow) = splitAt posX $ head changedBoard
let newRow = unchangedRow ++ "o" ++ tail changedRow
let newBoard = unchangedBoard ++ [newRow] ++ tail changedBoard
let finalOutput = reverse newBoard
mapM_ putStrLn finalOutput
Also note this code is very brittle as it uses partial functions all over the place (tail, head, read). You could try to use pattern matching instead to make the code more robust.

Haskell Implement an IO code

Im trying to implement an IO code with this concept:
Asking the user for the film title
checks if the film exist, if it does not it returns to the main menu
if it does the programs asks the user for the rating
it checks if its a valid integer, if it does
the program checks if the user has already voted for this film
if he does then asks if he want to modify the rating
if the user's input its not equal to "y" then the program should return to the main menu
if the user type "y" then the database should be updated with the current ratting.
I tried this :
if input /= "y"
then do return (username, database)
else do putStrLn "Your vote will be modified."
but I'm getting this error:
Couldn't match expected type `()'
with actual type `(String, Database)'
In the first argument of `return', namely `(username, database)'
In a stmt of a 'do' block: return (username, database)
In the expression: do { return (username, database) }
Failed, modules loaded: none.
if run it like that:
if input /= "y"
then do return (username, database)
else do putStrLn "Your vote will be modified."
even if the user's input not equals to "y" the database will be updated.
I can't figure out where's the problem with return (username, database)
The code is:
options 7 (username, database) = do
putStrLn "******************"
putStrLn " Rate a film "
putStrLn "******************"
putStrLn ""
putStr "Enter the title of the film or nothing to return to the main menu: "
title <- getLine
if title == ""
then return(username, database)
else do
let filmCheck = findFilm title database
if filmCheck == []
then do
putStrLn "That film does not exists."
return (username, database)
else do
putStr "Enter your rate: "
tempRate <- getLine
case reads tempRate :: [(Integer, String)] of
[(n, "")] -> do
let rate = read tempRate :: Int
let tempFilm = rateFilm username (username, rate) filmCheck
when (checkIfRated username tempFilm == True) $ do
putStrLn "You already voted for this film\n"
putStrLn "Do you want to modify your vote?\n"
putStrLn "Press y to modify or nothing to return to the main menu:"
input <- getLine
if input /= "y"
then do return (username, database)
else do putStrLn "Your vote will be modified."
let database = tempFilm:database
putStrLn "You rating has been sumbited successfully!"
putStrLn (displayFilm tempFilm)
return (username, database)
_ -> do
putStrLn "The number you entered is invalid."
return (username, database)
First, your indentation is wonky. Remember that indentation is significant in Haskell; if you get it wrong, you change the meaning of the code!
At first glance, I think the problem is here:
if input /= "y"
then do return (username, database)
else do putStrLn "Your vote iwll be modified."
The "do" keywords are actually redundant here. But much more importantly, putStrLn returns a (), whereas return clearly returns something else. There's your error.
As another aside, try breaking this into way, way smaller functions. This isn't Java...

Haskell Last generator in do {...} must be an expression error typed binding error

Im doing a small system from Haskell and im getting two errors called
"Last generator in do {...} must be an expression" and
"Type error in explicitly typed binding
Term : (fname,lname,cnic)
Type : (a,b,c)
Does not match : Database"
im new to haskell so plz help me out.
-------Data types-------
type FirstName = String
type LastName = String
type CustomerNIC = String
type Database = ( FirstName , LastName , CustomerNIC )
--type Details = [Database]
------Data base---------
exampleBase :: [Database]
exampleBase = [ ( "Kevin" , "Desilva" , "8929323V" ),( "Nimal" , "Perera" , "89120323V" ) ]
-------Main Menu-----------------------------
getInt :: IO Int
getInt = do line <- getline
return (read line :: Int)
selectsearch ::IO()
selectsearch = do
putStr"\n\n\t 1.Search by NIC:"
putStr"\n\n\t 2.Search by First Name:"
putStr"\n\n\t Your Chocie:"
input<-getInt
subsearch input
subsearch :: Int->IO()
subsearch x = do
if(x=1) then do
putStr"\n\t Enter NIC:"
cnic <- getLine
subsearch
else if (x=2) then do
putStr"\n\t Enter First Name:"
cnic <- getLine
subsearch
else if (x=3) then putStr "\n ERROR"
selectsearch
else MainMenu
------- Search ------------
getfName :: Database -> FirstName
getfName ( fname , lname , cnic ) = fname
searchByFirstName :: Database -> FirstName -> Database
searchByFirstName (a:ax) fname
| fname == getfName a = a
| length ax == 0 && getfName a/= fname = ("No Data","",0)
| otherwise = searchByFirstName ax fname
A few points:
There are weird indentation things throughout. Make sure you're using spaces and not tabs.
In subsearch, you often have cnic <- getLine (when you probably want getInt), but then not use that in the recursive call.
In your if statements, you should have x == 1, etc. rather than x = 1. Also consider using a case statement or guards rather than nested if-then-else.
You're missing a "do" in your x == 3 case.
getInt is identical to readLn.
Your searchByFirstName function could be written better.
A Database is made of three String fields, but your error case in searchByFirstName returns a value of type (String, String, Int).

Resources