I've been trying to take a user input string and read a dictionary into my main loop but whenever I try to check if my string is in the dictionary, I get a false return.
main = do
charStream <- getContents
contents <- readFile "/usr/share/dict/words"
let word = charStream
let listDict = lines contents
putStr (show (elem charStream listDict))
I would appreciate it if I could be pointed in the right direction. How do I take user input and read a file at the same time?
At a guess: your input stream has a newline in it. Use lines or words or similar on charStream, too.
Use getLine if you only need to read a single word. E.g. word <- getLine and modify the binding used with elem (currently charStream). If you want to keep using getContents: press Ctrl+d (EOF) after typing the word instead of Enter to end the stream.
Related
How can I write a string that contains newlines ("\n") to a file so that each string is on a new line in the file?
I have an accumulator function that iterates over some data and incrementally constructs a string (that contains information) for each element of the data. I don't want to write to the file every step so I'm appending the strings in each step. I do this so I can write the string in one time and limit the amount of IO.
Adding a newline to the string via str ++ "\n" doesn't work, hPrint h str will just print "\n" instead of starting on a new line.
I've tried accumulating a list of strings, instead of one big string, and iterating over the list and printing each string via hPrint. This works for the newlines but it also prints the quotation marks around each string on every line.
Don't use hPrint to write the strings to the file. Just like regular print it outputs the result of show, which produces a debugging-friendly version of the string with control characters and line endings escaped (and the surrounding quotes).
Use hPutStr or hPutStrLn instead. They will write the string to the file as-is (well, the latter adds a newline at the end).
The probably idiomatic solution to what you try to do is to simply aggregate the resulting strings in a list. Then, use the unlines prelude function which has the signature unlines :: [String] -> String and does your \n business for you.
Then, writing the string to disk can be done with help of writeFile which has the signature: writeFile :: FilePath -> String -> IO ().
Haskell is lazy. As such, it sometimes helps to think of Haskell lists as enumerators (C# like IEnumerable). This means here, that trying to compute line wise, then build the string manually and write it line by line is not really necessary. Just as readFile works lazily, so then does e.g. lines. In other words, you gain nothing if you try to "optimize" code which looks in its genuine form similar to this:
main = do
input <- readFile "infile"
writeFile "outfile" ((unlines . process) (lines input))
where
process inputLines = -- whatever you do
This question already has an answer here:
Why shouldn't I mix tabs and spaces?
(1 answer)
Closed 5 years ago.
i am learning Haskell from learnyouhaskell.com and got this error message when compiling my program.
Error:
baby.hs:25:26: error:
parse error on input `='
Perhaps you need a 'let' in a 'do' block?
e.g. 'let x = 5' instead of 'x = 5'
Code:
bump :: [String] -> IO ()
bump [fileName, numberString] = do
handle <- openFile fileName ReadMode
(tempName, tempHandle) <- openTempFile "." "temp"
contents <- hGetContents handle
let number = read numberString
text = lines contents
bumpLine = text !! number
newText = delete (text !! number) text
hPutStr tempHandle $ unlines (bumpLine:newText)
hClose handle
hClose tempHandle
removeFile fileName
renameFile tempName fileName
Can anyone help me with this?
As you verified the issue is that you are using tabs for indentation which is generally a bad idea, especially in languages where indentation is significant and especially when you mix tabs and spaces.
The compiler/interpreter has to convert those tab to a certain number of spaces to understand the indentation level of the code, and the conversion that it does can be different from what you are visually seeing and as such you can end up with code that looks fine to you, but not to the compiler/interpreter.
In this case it seems like the parser of ghc is not seeing the following declarations of your let inside the let, but as if they were like this:
let number = read numberString
text = lines contents
And it is telling you that the text = lines contents line is missing a let.
Just use spaces for Haskell and you'll never have this issue again.
I want to write a programm which would print out only one integer after applying some functions to the lines in text file, so far I have :
main = do
c <- getLine
let plot = plots (split ',' (change c))
print plot
main
where plots, split and change are the functions that convert input from string to int removing non-integer chars and then applying some calculations, the problem is that my input file has a lot of lines and I only managed to write a program which applies those functions to every line separately and prints out the result of every line,(I get as much output lines as there are input lines), but I want that this programm would sum up the results of every line and would print out only that number, where should I start or maybe somebody knows the solution? I am new in Haskell so please don't judge :/
Assuming that your conversion functions work for multi-line inputs, you can probably get away with simply replacing getLine with getContents and removing the recursive call to main. Although if you actually want to read a file, using readFile is probably cleaner than using getContents, since the latter is typically used for reading from the command line.
To expand upon what Paul wrote, getContents is like getLine, but it gives you the entire file in a single string. You can then get a list of lines using lines. For example:
main :: IO ()
main = do
contents <- getContents
print (lines contents)
When given the file
Haskell
is
neat!
will print ["Haskell","is","neat!"]. From here, you can map your line-handling function over that list to get a list of integers, and finally print (sum resultList).
Why doesn't the following program print my input? It seems that putStr is not taking the input. How does getContents work?
main = do
contents <- getContents
when (length contents < 10) $ putStr contents
However, this program prints the input line by line:
main = do
contents <- getContents
putStr contents
getContents gets everything. Linebuffering makes it line-by-line
getContents gets the entire input from a handle (eg file or user input), so your program
main = do
contents <- getContents
putStr contents
reads the entire contents of the standard input and prints it. The only reason you see this line by line is that it's using linebuffering on the terminal, so getContents gets its incoming String a line at the time.
Lazy evaluation
Haskell uses lazy evaluation, which means it only calculates something when it has to - it doesn't need calculate the end of the string you're printing to print the start of it, so it doesn't bother, and just prints what it has now. This lazy functions are able to return partial results when they can, rather than having to calculate everything first.
A line at a time
You appear in comments to want to only print lines when they're short like this session:
don't print this it's long
print this
print this
this is also too long
boo!
boo!
but since getContents is all the input, it won't print anything unless the total length is less than 10. What you were after was something where it treats each line separately, more like
main = do
contents <- getContents
putStrLn . unlines . filter short . lines $ contents
short xs = length xs <= 10
getContents reads all the stdin, but it works lazily. It returns a thunk, it is a promise to return some value when you ask it (force the thunk).
putStr asks for one char at a time, forcing the thunk to return value. For lists (note, String is a list of Chars), thunk when forced returns either "end of of list" or pair of ("next char", "thunk for the rest of the list"). So the second example works because putStr outputs chars when they become available. You enter the next line -- putStr outputs it char-by-char, tries to force the next thunk, but blocks because the next char is not available yet.
The first example forces the thunk until it returns "end of the list", because it is not possible to know string length until it is available.
If you're doing an interactive application, getting the length of getContents is a bad idea. That's because the length the standard input can only be calculated when the stream is closed. Which means that you'd have to either use Ctrl+D on Linux, Ctrl+Z on Windows or close the application, before you got to see the results.
Here is a function I wrote to break a long string into lines not longer than a given length
strBreakInLines <- function(s, breakAt=90, prepend="") {
words <- unlist(strsplit(s, " "))
if (length(words)<2) return(s)
wordLen <- unlist(Map(nchar, words))
lineLen <- wordLen[1]
res <- words[1]
lineBreak <- paste("\n", prepend, sep="")
for (i in 2:length(words)) {
lineLen <- lineLen+wordLen[i]
if (lineLen < breakAt)
res <- paste(res, words[i], sep=" ")
else {
res <- paste(res, words[i], sep=lineBreak)
lineLen <- 0
}
}
return(res)
}
It works for the problem I had; but I wonder if I can learn something here. Is there a shorter or more efficient solution, especially can I get rid of the for loop?
How about this:
gsub('(.{1,90})(\\s|$)', '\\1\n', s)
It will break string "s" into lines with maximum 90 chars (excluding the line break character "\n", but including inter-word spaces), unless there is a word itself exceeding 90 chars, then that word itself will occupy a whole line.
By the way, your function seems broken --- you should replace
lineLen <- 0
with
lineLen <- wordLen[i]
For the sake of completeness, Karsten W.'s comment points at strwrap, which is the easiest function to remember:
strwrap("Lorem ipsum... you know the routine", width=10)
and to match exactly the solution proposed in the question, the string has to be pasted afterwards:
paste(strwrap(s,90), collapse="\n")
This post is deliberately made community wiki since the honor of finding the function isn't mine.
For further completeness, there's:
stringi::stri_wrap
stringr::str_wrap (which just ultimately calls stringi::stri_wrap
The stringi version will deal with character sets better (it's built on the ICU library) and it's in C/C++ so it'll ultimately be faster than base::strwrap. It's also vectorized over the str parameter.
You can look at e.g. the write.dcf() FUNCTION in R itself; it also uses a loop so nothing to be ashamed of here.
The first goal is to get it right --- see Chambers (2008).