How to read three consecutive integers from stdin in Haskell? - haskell

I want to read an input like 12 34 56 into three integers using Haskell.
For a single integer, one might use myInteger <- readLn. But for this case, I have not found any solution, except the one of first reading a line, then replacing all spaces with ,, (using something like:
spaceToCommas str =
let repl ' ' = ','
repl c = c
in map repl str
) and then calling read '[' ++ str ++ ']' which feels very hackish. Also, it does not allow me to state that I want to read three integers, it will attempt to read any amount of integers from stdin.
There has to be a better way.
Note that I would like a solution that does not rely on external packages. Using e.g. Parsec is of course great, but this simple example should not require the use of a full-fledged Parser Combinator framework, right?

What about converting the string like:
convert :: Read a => String -> [a]
convert = map read . words
words splits the given string into a list of strings (the "words") and then we perform a read on every element using map.
and for instance use it like:
main = do
line <- getLine
let [a,b,c] = convert line :: [Int] in putStrLn (show (c,a,b))
or if you for instance want to read the first three elements and don't care about the rest (yes this apparently requires super-creativity skills):
main = do
line <- getLine
let (a:b:c:_) = convert line :: [Int] in putStrLn (show (c,a,b))
I here returned a tuple that is rotated one place to the right to show parsing is done.

Related

How to deal with "" within strings in haskell

I am currently creating a function in haskell like:
allStrings :: [String] -> String
What i want the function to do is take a list of strings and then split each string with a space character
so say input = ["Today", "Tomorrow"] output = "Today Tomorrow"
One problem i am running into is how to deal with strings that contain "" and char to represent new lines i am not sure how to deal with those i know that quote marks within strings are surrounding by \ what i want to do when we come across quote marks in our string is like so:
input ["Sun\"shine\" ", "Rain"] becomes output -> "Sun""shine" Rain"
is anyone able to help me with how to do this?
Thanks
The best method I'm aware of that joins Strings together in Haskell with a single whitespace is unwords.
Having the following signature:
λ> :t unwords
unwords :: [String] -> String
it perfectly suits your needs.
As for how a string containing a " is displayed (for example the output from unwords, if it contains a " will have a \", that's a printing issue.
If you want the \s to disappear while displaying the string, use putStrLn with the following signature:
λ> :t putStrLn
putStrLn :: String -> IO ()
Like so:
λ> putStrLn $ unwords ["Sun\"shine\" ", "Rain"]
Sun"shine" Rain

How to read line by line from a file in Haskell

im trying to make a programm that should read line by line from a file and check if its a palindrom, if it is, then print.
I'm really new to haskell so the only thing i could do is just print out each line, with this code :
main :: IO()
main = do
filecontent <- readFile "palindrom.txt"
mapM_ putStrLn (lines filecontent)
isPalindrom w = w==reverse w
The thing is, i dont know how to go line by line and check if the line is a palindrom ( note that in my file, each line contains only one word). Thanks for any help.
Here is one suggested approach
main :: IO()
main = do
filecontent <- readFile "palindrom.txt"
putStrLn (unlines $ filter isPalindrome $ lines filecontent)
isPalindrome w = w==reverse w
The part in parens is pure code, it has type String->String. It is generally a good idea to isolate pure code as much as possible, because that code tends to be the easiest to reason about, and often is more easily reusable.
You can think of data as flowing from right to left in that section, broken apart by the ($) operators. First you split the content into separate lines, then filter only the palindromes, finally rebuild the full output as a string. Also, because Haskell is lazy, even though it looks like it is treating the input as a single String in memory, it actually is only pulling the data as needed.
Edited to add extra info....
OK, so the heart of the soln is the pure portion:
unlines $ filter isPalindrome $ lines filecontent
The way that ($) works is by evaluating the function to the right, then using that as the input of the stuff on the left. In this case, filecontent is the full input from the file (a String, including newline chars), and the output is STDOUT (also a full string including newline chars).
Let's follow sample input through this process, "abcba\n1234\nK"
unlines $ filter isPalindrome $ lines "abcba\n1234\nK"
First, lines will break this into an array of lines
unlines $ filter isPalindrome ["abcba", "1234", "K"]
Note that the output of lines is being fed into the input for filter.
So, what does filter do? Notice its type
filter :: (a -> Bool) -> [a] -> [a]
This takes 2 input params, the first is a function (which isPalendrome is), the second a list of items. It will test each item in the list using the function, and its output is the same list input, minus items that the function has chosen to remove (returned False on). In our case, the first and third items are in fact palendromes, the second not. Our expression evaluates as follows
unlines ["abcba", "K"]
Finally, unlines is the opposite of lines.... It will concatinate the items again, inserting newlines in between.
"abcba\nK"
Since STDIO itself is a String, this is ready for outputting.
Note that is it perfectly OK to output a list of Strings using non-pure functions, as follows
forM ["1", "2", "3"] $ \item -> do
putStrLn item
This method however mixes pure and impure code, and is considered slightly less idiomatic Haskell code than the former. You will still see this type of thing a lot though!
Have a look at the filter function. You may not want to put all processing on a single line, but use a let expression. Also, your indentation is off:
main :: IO ()
main = do
filecontent <- readFile "palindrom.txt"
let selected = filter ... filecontent
...

Haskell interact function

I’m new to Haskell and have a problem with interact function. This is my sample program:
main :: IO ()
main = interact inputLength
inputLength :: String -> String
inputLength input = show $ length input
It compiles but when running doesn’t print the output - just prints the string that is passed to it and moves to the next line. When I pass the interact another String -> String function like this:
upperCase :: String -> String
upperCase input = map toUpper input
it runs ok and prints the argument in uppercase as expected – so what is wrong with the first function?
The String -> String argument given to interact should take a string containing all the input and return a string containing all the output. The reason you see output after pressing enter with interact (map toUpper) is because map toUpper acts lazily -- it can start giving output before all the input is known. Finding the length of a string is not like this -- the whole string must be known before any output can be produced.
You need to either signal an EOF to say that you are done entering input (in the console, this is Control-D on Unix/Mac systems, I believe it's Control-Z on Windows), then it will give you the length. Or you can find the length of each line by saying so:
interact (unlines . map inputLength . lines)
This will always be lazy in each line, so you know you can get one output after each input.
Since acting on lines is such a common pattern, I like to define a little helper function:
eachLine :: (String -> String) -> (String -> String)
eachLine f = unlines . map f . lines
Then you can do:
main = interact (eachLine inputLength)
A more reusable solution:
main = interactLineByLine processLine
-- this wrapper does the boring thing of mapping, unlining etc.... you have to do all the times for user interaction
interactLineByLine:: (String -> String) -> IO ()
interactLineByLine f = interact (unlines . (map processLine) . lines)
-- this function does the actual work line by line, i.e. what is
-- really desired most of the times
processLine:: String -> String
processLine line = "<" ++ line ++ ">"

How to read text files into a haskell program?

I have a text file which contains two lists on each line. Each list can contain any number of alphanumeric arguments.
eg [t1,t2,...] [m1,m2,...]
I can read the file into ghc, but how can I read this into another main file and how can the main file recognise each argument separately to then process it?
I think it's best for you to figure out most of this for yourself, but I've got some pointers for you.
Firstly, try not to deal with the file access until you've got the rest of the code working, otherwise you might end up having IO all over the place. Start with some sample data:
sampleData = "[m1,m2,m3][x1,x2,x3,x4]\n[f3,f4,f5][y7,y8,y123]\n[m4,m5,m6][x5,x6,x7,x8]"
You should not mention sampleData anywhere else in your code, but you should use it in ghci for testing.
Once you have a function that does everything you want, eg processLists::String->[(String,String)], you can replcae readFile "data.txt" :: IO String with
readInLists :: FilePath -> IO [(String,String)]
readInLists filename = fmap processLists (readFile filename)
If fmap makes no sense to you, you could read a tutorial I accidentally wrote.
If they really are alphanumeric, you can split them quite easily. Here are some handy functions, with examples.
tail :: [a] -> [a]
tail "(This)" = "This)"
You can use that to throw away something you don't want at the front of your string.
break :: (Char->Bool) -> String -> (String,String)
break (== ' ') "Hello Mum" = ("Hello"," Mum")
So break uses a test to find the first character of the second string, and breaks the string just before it.
Notice that the break character is still there at the front of the next string. span is the same but uses a test for what to have in the first list, so
span :: (Char->Bool) -> String -> (String,String)
span (/= ' ') "Hello Mum" = ("Hello"," Mum")
You can use these functions with things like (==','), or isAlphaNum (you'll have to import Data.Char at the top of your file to use it).
You might want to look at the functions splitWith and splitOn that I have in this answer. They're based on the definitions of split and words from the Prelude.

Finding and replacing words with asterisk, in a text file output

Hello I am new at Haskell and i'm having problems trying to get this script to work. This script reads in arguements from a command line and find them in a seperate text file.
E.G: cat.txt | ./redact house big cat (in compiler)
It redacts certain words in a text file by replacing them with stars (**)asterisks. The number of stars used for each redacted word should equal the number of characters in the word.
module Main where
import System
import Data.Char
import Data.List
lowercase :: String -> String
lowercase = map toLower
main = do
arg1 <- getArgs
txt <- getContents
putStr (redact txt arg1)
redact :: String -> String -> String
redact input xWords = unlines [ work line | line <- lines input ]
where work line = unwords [ foo word | word <- words line ]
foo w | lowercase(w) == lowercase(xWords) = convertWord w 1
| otherwise = w
convertWord Eq a => [a] -> [a]
convertWord = map (const '*')
However, when i try to compile this, GHCi returns the error:
redact.hs:13:38:
Couldn't match expected thye 'Char' with actual type '[Char]'
Expected type: String
Actual type: [String]
In the second argument of 'redact', namely 'arg1'
In the first of 'putStr', namely '<redact txt arg1>'
Failed, module loaded: none.
So the code:
putStr (redact txt arg1)
is causing the problem.
Thank you in advance for any help and if you can improve the code in anyway that would be great.
EDIT:
I want to enter as many args as possible, it doesnt matter how many args you enter, i tried:
(arg1:arg2:arg3:arg4:arg5:_) <- getArgs
but I have to enter EXACT 5 args, It shouldn't matter how many args I enter.
I was thinking of using some kind of loop but I am not sure?
Again thank you for your help.
To get it to work with multiple arguments, use getArgs as you have it. The problem lies with
foo w | lowercase(w) == lowercase(xWords) = convertWord w 1
| otherwise = w
where you compare the lowercase of one word to lowercase of multiple words. The latter is not defined, you'd like to compare it to the lowercase of each of the xWords. So first you need to bring them all to lowercase, that's most efficiently done by calling from main redact txt (map lowercase arg1) rather than just redact txt arg1. Then you need to determine if a read word is in the list xWords, that's what the elem function is there for.
foo w | lowercase w `elem` xWords = convertWord w 1
| otherwise = w
BTW, you should maybe not call this function foo even if it's only a local one.
getArgs :: IO [String], so after arg1 <- getArgs, arg1 has the type [String]: it contains all the arguments passed to your program, as a list. But you're using it as String, thus the error: GHC expected arg1 to be a String, but it's a [String].
You can pattern-match on the result like this:
arg1:_ <- getArgs
This results in arg1 containing the first element of the list, and discards the rest of the list. If you don't pass an argument, it'll result in a runtime error. Of course, if you want more specialised behaviour (say, printing an error when no arguments are given), you could use a more complex method of extracting the first argument, such as a case expression.
As far as improvements to your program go:
You can simplify the definition of work using function composition and map rather than the list comprehension: work = unwords . map foo . words (read: "map foo over all the elements of the words, then unwords them").
redact can be simplified similarly, to redact input xWords = unlines . map work . lines $ input.
lowercase(w) is better written as lowercase w.
But your program looks basically fine to me, apart from some oddities (like the missing :: in convertWord's type signature, the additional 1 you pass to it in foo — but going by the somewhat erratic indentation, I guess you edited the code before posting it). I wouldn't make the first two changes unless you understand how they work and are comfortable writing code like that.

Resources