I am writing a haskell program and am having trouble with IO types. It seems something is wrong with my readLines function and the type it is supposed to return. I want it to return a list where each element is a line from the file that is opened with the openDict function.
Here is my code.
main :: IO ()
main = do
fileHandle <- openDict
readLines fileHandle
putStr "End of program\n"
readLines :: Handle -> [IO String]
readLines fileHandle
| isEOF <- hIsEOF fileHandle = []
| otherwise = [hGetLine fileHandle] ++ readLines fileHandle
openDict :: IO Handle
openDict = do
putStr "Enter file path: "
filePath <- getLine
openFile filePath ReadMode
Here is the error:
Couldn't match type `[]' with `IO'
Expected type: IO (IO String)
Actual type: [IO String]
In the return type of a call of `readLines'
In a stmt of a 'do' block: readLines fileHandle
In the expression:
do { fileHandle <- openDict;
readLines fileHandle;
putStr "End of program" }
So basically, how can I store these strings from IO in a list?
Your readLines function has several problems.
First, in this case you do not want to return a [IO String]. This would be a list of IO actions such as [print 1 >> return "a", print 2 >> return "b"] which is not what you want. You need a single IO action returning a list of Strings, so you need to use IO [String].
Second, the pattern guard isEof <- hIsEOF fileHandle is checking whether the value returned by hIsEOF (which is an IO action) is of the form isEOF, where isEOF is a variable being defined right now. Of course anything is of the form var, since a variable can be anything. So the test is always true, and not meaningful: it does not even run the IO action, for instance.
Third, the way you combine the list constructors and IO actions follows the current wrong type,
so it is quite confusing.
A quite verbose way to achieve this would be instead:
readLines :: Handle -> IO [String]
readLines fileHandle = do
eof <- hIsEOF fileHandle
if eof then return []
else do line <- hGetLine fileHandle
rest <- readLines fileHandle
return (line:rest)
The last lines can be shortened a bit using Applicative syntax:
import Control.Applicative
readLines2 :: Handle -> IO [String]
readLines2 fileHandle = do
eof <- hIsEOF fileHandle
if eof then return []
else (:) <$> hGetLine fileHandle <*> readLines2 fileHandle
A short version can be instead obtained by reading everything in the file and splitting into lists after that using the lines library function.
import Control.Applicative
readLines3 :: Handle -> IO [String]
readLines3 fileHandle = lines <$> hGetContents fileHandle
Here is the readLines that you likely really want.
readLines :: Handle -> IO [String]
readLines h = do
isEOF <- hIsEOF h -- need to access isEOF directly, without monad, see comment about the bind
case isEOF of
True -> return [] -- the return adds the IO monad to the [String] type
False -> do
line <- hGetLine h -- need to access line separately without monad
lines <- readLines h -- recurse down
return $ line ++ lines -- return adds back the IO monad
Related
I found this code online, but it's not running.
main = do
xs <- getLine []
print xs
So how do I ask the user for list input in Haskell? I am new to Haskell, please explain when you answer. Thanks.
You do it e.g. like this:
main :: IO ()
main = do
xs <- getLine
let { ints :: [Int]
; ints = read xs
}
print $ take 2 ints
and you must type in the input in a valid list syntax, e.g.
[1,2,3]
Do take note, each line in a do-block must start at the same indentation level (unless explicit separators { ; } are used).
getLine is an IO action that produces a string, nothing else. You need to process that string once you receive it. As an example, here's an IO action that will parse an appropriate input into a list of Int values.
getIntList :: IO [Int]
getIntList = fmap read getLine
main = do
ints <- getIntList
print ints
There is a Read instance for lists, so the following works:
> read "[1,2,3]" :: [Int]
[1,2,3]
getIntList uses the Functor instance for IO to apply read to the string that getLine will produce; read's concrete type will be inferred from the type given to getIntList: since getIntList :: IO [Int], then fmap read :: IO String -> IO [Int], and so read :: String -> [Int] will be used.
I am trying to write a simple function which reads in one line at a time (which I know will be integers) and then stores them in to a list. For the life of me however, it seems like the list comes out to empty.
import System.IO
import Control.Monad
type Int2 = [Int]
valueTab = [] :: [Int]
app [ ] list = list
app (h:t) list = h:(app t list)
main :: IO ()
main = do
hSetBuffering stdout NoBuffering -- DO NOT REMOVE
-- Auto-generated code below aims at helping you parse
-- the standard input according to the problem statement.
input_line <- getLine
let n = read input_line :: Int
let value = [] :: [Int]
replicateM n $ do
input_line <- getLine
let pi = read input_line :: Int
hPutStrLn stderr (show input_line)
hPutStrLn stderr (show valueTab)
return $ app valueTab [pi]
-- hPutStrLn stderr "Debug messages..."
-- Write answer to stdout
--putStrLn input_line
return ()
So when I run this with
8
6
4
3 all on their own lines,
It prints 6, [], 4, [], 3 [].
Is this a problem with my printing, with my list declaration, with the way that I store them or? I have both value and valueTab to check whether it was a scope problem.
Note: The code is boilerplate code on a coding website that tests it on it's platform. Assume replicateM is just a loop that runs through the code x number of times.
It looks like you're doing a problem on codinggame.com. Other coding sites seem to do a better job with their Haskell templates, particularly for beginner exercises -- the template usually takes care of all the input and output, and you just need to supply the missing pure function. In contrast, codinggame.com's Haskell templates seem to assume the beginner has a pretty firm grasp of the IO monad, and leave out a lot of essential details (e.g., the fact that the result of the replicateM action should actually be bound to a variable) which you're expected to fill in.
The replicateM call in the original template probably looked something like:
replicateM n $ do
input_line <- getLine
let pi = read input_line :: Int -- maybe this line was there, maybe not
return ()
This expression creates a composite IO action that, when executed, will repeat the following subaction n times:
read a line, binding the string read to input_line
prepare to convert that line to an integer value pi (though this is never done because pi isn't used)
regardless of the string read, return "unit" (the value ()) as the result of the subaction
The value of the composite action is then a list of the values returned by the subactions. Since those were all units, the final value of the composite action is a list [(),(),()...()], with one () for each line read. However, because the value of this composite action is never bound to a variable (i.e., because there's no result <- ... before the replicateM expression), this list is thrown away.
So, this template provides a needlessly verbose method of reading n lines of input and doing nothing with them.
Fortunately, all you need to do to make this template do something useful is to have the subaction return a value that's useful (e.g., the integer pi) which will cause the composite action to return a list of the integers read, and then make sure you bind the resulting list to a variable using the <- notation.
In other words, you want to write:
main = do
...
pis <- replicateM n $ do
input_line <- getLine
let pi = read input_line :: Int
return pi
hPutStrLn stderr (show pis)
You won't need the helper function app, and you won't need to pre-declare a list valueTab to contain the result. The result is automatically produced by replicateM, and you just need to name it so you can use it.
The complete working program will look like:
import System.IO
import Control.Monad
type Int2 = [Int]
main :: IO ()
main = do
hSetBuffering stdout NoBuffering -- DO NOT REMOVE
-- Auto-generated code below aims at helping you parse
-- the standard input according to the problem statement.
input_line <- getLine
let n = read input_line :: Int
let value = [] :: [Int]
pis <- replicateM n $ do
input_line <- getLine
let pi = read input_line :: Int
return pi
hPutStrLn stderr (show pis)
-- hPutStrLn stderr "Debug messages..."
-- Write answer to stdout
return ()
I am basically trying to read a large file (around 10G) into a list of lines. The file contains a sequence of integer, something like this:
0x123456
0x123123
0x123123
.....
I used the method below to read files by default for my codebase, but it turns out to be quit slow (~12 minutes) at this scenario
let lines_from_file (filename : string) : string list =
let lines = ref [] in
let chan = open_in filename in
try
while true; do
lines := input_line chan :: !lines
done; []
with End_of_file ->
close_in chan;
List.rev !lines;;
I guess I need to read the file into memory, and then split them into lines (I am using a 128G server, so it should be fine for the memory space). But I still didn't understand whether OCaml provides such facility after searching the documents here.
So here is my question:
Given my situation, how to read files into string list in a fast way?
How about using stream? But I need to adjust related application code, then that could cause some time.
First of all you should consider whether you really need to have all the information at once in your memory. Maybe it is better to process file line-by-line?
If you really want to have it all at once in memory, then you can use Bigarray's map_file function to map a file as an array of characters. And then do something with it.
Also, as I see, this file contains numbers. Maybe it is better to allocate the array (or even better a bigarray) and the process each line in order and store integers in the (big)array.
I often use the two following function to read the lines of a file. Note that the function lines_from_files is tail-recursive.
let read_line i = try Some (input_line i) with End_of_file -> None
let lines_from_files filename =
let rec lines_from_files_aux i acc = match (read_line i) with
| None -> List.rev acc
| Some s -> lines_from_files_aux i (s :: acc) in
lines_from_files_aux (open_in filename) []
let () =
lines_from_files "foo"
|> List.iter (Printf.printf "lines = %s\n")
This should work:
let rec ints_from_file fdesc =
try
let l = input_line fdesc in
let l' = int_of_string l in
l' :: ints_from_file fdesc
with | _ -> []
This solution converts the strings to integers as they're read in (which should be a bit more memory efficient, and I assume this was going to be done to them eventually.
Also, because it is recursive, the file must be opened outside of the function call.
I have a function that takes two filenames, and reads the contents of those two files into Strings, and then returns if they match or not. Here's the function:
f :: String -> String -> IO Bool
f fileName1 fileName2 = do
str1 <- readFile fileName1
str2 <- readFile fileName2
return (str1 == str2)
And if I use it like this from inside main:
main = do
res <- f "A.txt" "B.txt"
print res
It works and prints either True or False. What I want to do is, apply this function f to a list (of tuples) of filenames. For something like:
[("a.txt", "b.txt"), ("c.txt", "d.txt")]
(Assume that a.txt and b.txt have the same content and c.txt and d.txt are different).
I want to transform it (the list of file names) into a Bool list like: [True, False]. I tried using mapM, but that doesn't appear to map anything (when I print the list after using mapM, it prints the same list of tuples).
So my question is: What am I doing wrong, and how can I get a list of Bools like I mentioned above?
Please go easy on me as I'm still quite new to Haskell and functional programming :)
Here is a function f' which does what you describe.
f' :: [(String,String)] -> IO [Bool]
f' = mapM $ uncurry f
Let me know if something is unclear! And, just to be clear, here is how you run it:
main = do
res <- f' [("a.txt", "b.txt"), ("c.txt", "d.txt")]
print res
EDIT
The function is in pointfree form, so it is equivalent to f' lst = mapM (uncurry f) lst. mapM essentially maps each element of lst using f as the function, and pushes the IO to the outside of the list.
uncurry just takes a function of the form a -> b -> c and transforms it into one (a,b) -> c, which is what we want since you have a list of tuples.
I am trying to learn input output in sml.In an effort to copy strings of lsthat are the same as s1 into the file l2 I did the following.I am getting some errors I can not really understand.Can someone help me out.
fun test(l2:string,ls:string list,s1:string) = if (String.isSubstring(s1 hd(ls))) then
(TextIO.openOut l2; TextIO.inputLine hd(ls))::test(l2,tl(ls),s1) else
test(l2,tl(ls),s1);
Here are some general hints:
Name your variables something meaningful, like filename, lines and line.
The function TextIO.inputLine takes as argument a value of type instream.
When you write TextIO.inputLine hd(ls), what this is actually interpreted as is
(TextIO.inputLine hd) ls, which means "treat hd as if it were an instream and
try and read a line from it, take that line and treat it as if it were a function,
and apply it on ls", which is of course complete nonsense.
The proper parenthesising in this case would be TextIO.inputLine (hd ls), which
still does not make sense, since we decided that ls is a string list, and so hd ls
will be a string and not an instream.
Here is something that resembles what you want to do, but opposite:
(* Open a file, read each line from file and return those that contain mySubstr *)
fun test (filename, mySubstr) =
let val instr = TextIO.openIn filename
fun loop () = case TextIO.inputLine instr of
SOME line => if String.isSubstring mySubstr line
then line :: loop () else loop ()
| NONE => []
val lines = loop ()
val _ = TextIO.closeIn instr
in lines end
You need to use TextIO.openOut and TextIO.output instead. TextIO.inputLine is one that reads from files.