I have text file containing data like that:
13.
13.
[(1,2),(2,3),(4,5)].
And I want to read this into 3 variables in Haskell. But standard functions read this as strings, but considering I get rid of dot at the end myself is there any built-in parser function that will make Integer of "13" and [(Integer,Integer)] list out of [(1,2),(2,3),(4,5)] ?
Yes, it's called read:
let i = read "13" :: Integer
let ts = read "[(1,2),(2,3),(4,5)]" :: [(Integer, Integer)]
The example text file you gave has trailing spaces as well as the full stop, so merely cutting the last character doesn't work. Let's take just the digits, using:
import Data.Char (isDigit)
Why not have a data type to store the stuff from the file:
data MyStuff = MyStuff {firstNum :: Int,
secondNum:: Int,
intPairList :: [(Integer, Integer)]}
deriving (Show,Read)
Now we need to read the file, and then turn it into individual lines:
getMyStuff :: FilePath -> IO MyStuff
getMyStuff filename = do
rawdata <- readFile filename
let [i1,i2,list] = lines rawdata
return $ MyStuff (read $ takeWhile isDigit i1) (read $ takeWhile isDigit i2) (read $ init list)
The read function works with any data type that has a Read instance, and automatically produces data of the right type.
> getMyStuff "data.txt" >>= print
MyStuff {firstNum = 13, secondNum = 13, intPairList = [(1,2),(2,3),(4,5)]}
A better way
I'd be inclined to save myself a fair bit of work, and just write that data directly, so
writeMyStuff :: FilePath -> MyStuff -> IO ()
writeMyStuff filename somedata = writeFile filename (show somedata)
readMyStuff :: FilePath -> IO MyStuff
readMyStuff filename = fmap read (readFile filename)
(The fmap just applies the pure function read to the output of the readFile.)
> writeMyStuff "test.txt" MyStuff {firstNum=12,secondNum=42, intPairList=[(1,2),(3,4)]}
> readMyStuff "test.txt" >>= print
MyStuff {firstNum = 12, secondNum = 42, intPairList = [(1,2),(3,4)]}
You're far less likely to make little parsing or printing errors if you let the compiler sort it all out for you, it's less code, and simpler.
Haskell's strong types require you to know what you're getting. So let's forgo all error checking and optimization and assume that the file is always in the right format, you can do something like this:
data Entry = Number Integer
| List [(Integer, Integer)]
parseYourFile :: FilePath -> IO [Entry]
parseYourFile p = do
content <- readFile p
return $ parseYourFormat content
parseYourFormat :: String -> [Entry]
parseYourFormat data = map parseEntry $ lines data
parseEntry :: String -> Entry
parseEntry line = if head line == '['
then List $ read core
else Number $ read core
where core = init line
Or you could write a proper parser for it using one of the many combinator frameworks.
Related
First of all, sorry for my bad english. I'm not native and try my best :)
Now to the problem: i have a list of Strings and want to convert them to a list of integers. The Problem is, it's not just numbers, basically the String is a List to.
["[1,2,3,4,5,6,7,8]","[8,7,6,5,4,3,2,1]","[1,2,3,4,5,6,7,8]"]
This is the result i get from my code i'll post further down.
Any idea how i can achieve, that the internal list of numbers are list of integers?
I tried like three hours and didn't find a solution.
Every help is appreciatet.
Kind regards
get "/authors/:author" $ do
authorName <- param "author"
directories <- liftIO(listDirectory("data/" ++ authorName))
liftIO(readFiles directories authorName)
html (T.pack (HtmlModule.h1 ("Author: " ++ authorName)))
readFiles :: [String] -> String -> IO ()
readFiles x authorName = do
let y = addPrefix x authorName
content <- mapM readFile y
putStrLn (show content)
Result: ["[1,2,3,4,5,6,7,8]","[8,7,6,5,4,3,2,1]","[1,2,3,4,5,6,7,8]"]
You can read the string into a list of ints:
let nums = map read content :: [[Int]]
You can use read :: Read a => String -> a to convert a string to a type that is a member of the Read typeclass.
Since Int is a member of the Read typeclass, and [a] is a member of the Read typeclass if a is a member of the Read typeclass, we thus can read a list of Ints:
Prelude> read "[1,2,3,4,5,6,7,8]" :: [Int]
[1,2,3,4,5,6,7,8]
We thus can convert a list of Strings with:
content <- mapM ((read :: String -> [Int]) . readFile) y
read will raise an error in case the String can not be converted. You can make use of readMaybe :: Read a => String -> Maybe a to wrap the result in a Just in case parsing was successful, and Nothing in case parsing failed.
I am having trouble reading in a level file in Haskell. The goal is to read in a simple txt file with two numbers seperated by a space and then commas. The problem I keep getting is this: Couldn't match type `IO' with `[]'
If I understand correctly the do statement is supposed to pull the String out of the Monad.
readLevelFile :: FilePath -> [FallingRegion]
readLevelFile f = do
fileContent <- readFile f
(map lineToFallingRegion (lines fileContent))
lineToFallingRegion :: String -> FallingRegion
lineToFallingRegion s = map textShapeToFallingShape (splitOn' (==',') s)
textShapeToFallingShape :: String -> FallingShape
textShapeToFallingShape s = FallingShape (read $ head numbers) (read $ head
$ tail numbers)
where numbers = splitOn' (==' ') s
You can't pull things out of IO. You can think of IO as a container (in fact, some interpretations of IO liken it to the box containing Schrödinger's cat). You can't see what's in the container, but if you step into the container, values become visible.
So this should work:
readLevelFile f = do
fileContent <- readFile f
return (map lineToFallingRegion (lines fileContent))
It does not, however, have the type given in the OP. Inside the do block, fileContent is a String value, but the entire block is still inside the IO container.
This means that the return type of the function isn't [FallingRegion], but IO [FallingRegion]. So if you change the type annotation for readLevelFile to
readLevelFile :: FilePath -> IO [FallingRegion]
you should be able to get past the first hurdle.
Let's look at your first function with explicit types:
readLevelFile f = do
(fileContent :: String) <-
(readFile :: String -> IO String) (f :: String) :: IO String
fileContent is indeed of type String but is only available within the execution of the IO Monad under which we are evaluating. Now what?
(map lineToFallingRegion (lines fileContent)) :: [String]
Now you are suddenly using an expression that is not an IO monad but instead is a list value - since lists are also a type of monad the type check tries to unify IO with []. What you actually wanted is to return this value:
return (map lineToFallingRegion (lines fileContent)) :: IO [String]
Now recalling that we can't ever "exit" the IO monad your readLevelFile type must be IO - an honest admission that it interacts with the outside world:
readLevelFile :: FilePath -> IO [FallingRegion]
I'm new to Haskell and IO is still a bit confusing. I have a txt file that I want to read, add the numbers in the text file, and then write it to a text file. the file looks like the following:
2
3
the numbers are separated by a new line character I know how to read a file contents then write it to another file but I don't know how I can manipulate it or if I have to cast the information to an Int?
module Main where
import System.Environment
-- | this fuction read first line in a file and write out to src file
-- src "src.txt", des "des.txt"
copyFirstLine :: FilePath -- ^ path to input file
-> FilePath -- ^ path to output file
-> IO ()
copyFirstLine src dst = do
contect <- readFile src
let (fst :rest) = (lines contect)
writeFile dst fst
main = do
[src,dst] <- getArgs
copyFirstLine src dst
Thanks in advance.
I can't sure your 'manipulate' means what, but I will assume you need integer calculation. It won't be difficult to manipulate as string.
If you hoogle the signature String -> Int you can find the read.
-- | this fuction read first line in a file and write out +1 result
-- to src file src "src.txt", des "des.txt"
eachPlusOne :: FilePath -- ^ path to input file
-> FilePath -- ^ path to output file
-> IO ()
eachPlusOne src dst = do
contect <- readFile src
let lns = lines contect :: [String]
ints = map ((1+) . read) lns :: [Int]
outs = unlines . map show $ ints :: String
writeFile dst outs
If you are using sufficiently recent version of ghc, you can use readMaybe which is desirable.
I have a main like the following:
main :: IO ()
main = do
args <- getArgs
putStrLn $ functionName args
where
functionName args = "problem" ++ (filter (/= '"') $ show (args!!0))
Instead of putting the name to stdout like I do it right now, I want to call the function.
I am aware of the fact, that I could use hint (as mentioned in Haskell: how to evaluate a String like "1+2") but I think that would be pretty overkill for just getting that simple function name.
At the current stage it does not matter if the program crashes if the function does not exist!
Without taking special measures to preserve them, the names of functions will likely be gone completely in a compiled Haskell program.
I would suggest just making a big top-level map:
import Data.Map ( Map )
import qualified Data.Map as Map
functions :: Map String (IO ())
functions = Map.fromList [("problem1", problem1), ...]
call :: String -> IO ()
call name =
case Map.lookup name of
Nothing -> fail $ name + " not found"
Just m -> m
main :: IO ()
main = do
args <- getArgs
call $ functionName args
where
functionName args = "problem" ++ (filter (/= '"') $ show (args!!0))
If you're going to do this, you have a few approaches, but the easiest by far is to just pattern match on it
This method requires that all of your functions you want to call have the same type signature:
problem1 :: Int
problem1 = 1
problem2 :: Int
problem2 = 2
runFunc :: String -> Maybe Int
runFunc "problem1" = Just problem1
runFunc "problem2" = Just problem2
runFunc _ = Nothing
main = do
args <- getArgs
putStrLn $ runFunc $ functionName args
This requires you to add a line to runFunc each time you add a new problemN, but that's pretty manageable.
You can't get a string representation of an identifier, not without fancy non-standard features, because that information isn't retained after compilation. As such, you're going to have to write down those function names as string constants somewhere.
If the function definitions are all in one file anyway, what I would suggest is to use data types and lambdas to avoid having to duplicate those function names altogether:
Data Problem = {
problemName :: String,
evalProblem :: IO () # Or whatever your problem function signatures are
}
problems = [Problem]
problems = [
Problem {
problemName = "problem1",
evalProblem = do ... # Insert code here
},
Problem
problemName = "problem2",
evalProblem = do ... # Insert code here
}
]
main :: IO ()
main = do
args <- getArgs
case find (\x -> problemName x == (args!!0)) problems of
Just x -> evalProblem x
Nothing -> # Handle error
Edit: Just to clarify, I'd say the important takeaway here is that you have an XY Problem.
How do I use the Language.Haskell.Interpreter to read the given config file and assign the values given in it to initialize variables in my program?
My config file is like:
numRecords = 10
numFields = 3
inputFile = /home/user1/project/indata.file
outputFile = /home/user1/project/outdata.file
datefmt = ddmmyyyy
I want to initialize the variables corresponding to the identifiers given in the config file with the values given in the config file.
How do I use the Language.Haskell.Interpreter to accomplish this thing?
I am confused because of the IO Monad and the Interpreter Monad. A small example of this kind will also be useful.
Why not?
data Config = Config {size :: Int, path :: String} deriving (Read, Show)
readConfig :: String -> Config
readConfig = read
main = do
config <- readFile "my.config" >>= return . readConfig
putStrLn $ "Configured size := " ++ show (size config)
putStrLn $ "Configured path := " ++ show (path config)
Using my.config file
Config {
size = 1024,
path = "/root/passwords.db"
}
And testing using ghci
*Main> main
Configured size := 1024
Configured path := "/root/passwords.db"
*Main>
(sorry previous bugs, I was hurry)
First of all, you cannot (in any obvious way, at least) use Language.Haskell.Interpreter from the hint package to do this. That functions in that module are used to read in and run Haskell code, not arbitrary structured data.
For reading in structured data, you will need a parser of some sort. Here are some of the options that you have:
Use a parser generator such as Happy.
Use a parser-combinator library such as uu-parsinglib or parsec.
Directly implement your own parser.
Make use of automatically derived instances of the Read class.
Ad 1. and 2.
If the format of the data you need to read in is nontrivial or if you need helpful error messages in case parsing fails, I'd recommend to go for 1. or 2. and to consult the documentation of the respective tools and libraries. Be aware that you will need some time to get accustomed to their main concepts and interfaces.
Ad 3.
If the format of your data is simple enough (as it is in your example) and if extensive error reporting is not high on your wish list, you can easily roll your own parser.
In your example, a config file is essentially a listing of keys and values, seperated by newlines. In Haskell, we can represent such a listing by a list of pairs of strings:
type Config = [(String, String)]
"Parsing" a configuration then reduces to: (1) splitting the input string in lines, (2) splitting each line in words, (3) selecting from each line the first and the third word:
readConfig :: String -> Config
readConfig s =
[(key, val) | line <- lines s, let (key : _ : val : _) = words line]
To retrieve an entry from a parsed configuration file, we can then use a function get:
get :: String -> (String -> a) -> Config -> a
get key f config = case lookup key config of
Nothing -> error ("get: not found: " ++ key)
Just x -> f x
This function takes as its first argument the key of the entry and as its second argument a function that converts the raw value string into something of the appropriate type. For purely textual configuration values we can simply pass the identity function to get:
inputFile, outputFile, datefmt :: Config -> String
inputFile = get "inputFile" id
outputFile = get "outputFile" id
datefmt = get "datefmt" id
For integer entries we can use read:
numRecords, numFields :: Config -> Int
numRecords = get "numRecords" read
numFields = get "numFields" read
Perhaps these patterns are common enough to be factored out into their own dedicated versions of get:
getS :: String -> Config -> String
getS key = get key id
getR :: Read a => String -> Config -> a
getR key = get key read
inputFile', outputFile', datefmt' :: Config -> String
inputFile' = getS "inputFile"
outputFile' = getS "outputFile"
datefmt' = getS "datefmt"
numRecords', numFields' :: Config -> Int
numRecords' = getR "numRecords"
numFields' = getR "numFields"
As an example, here is program that reads in a configuration file and that prints the value for "outputFile":
main :: IO ()
main = do
s <- readFile "config.txt"
let config = readConfig s
putStrLn (outputFile config)
Ad 4.
If you can control the format of the configuration file, you can introduce a new datatype for holding configuration data and have Haskell automatically derive an instance of the class Read for it. For instance:
data Config = Config
{ numRecords :: Int
, numFields :: Int
, inputFile :: String
, outputFile :: String
, datefmt :: String
} deriving Read
Now you will need to make sure that your configuration files match the expected format. For instance:
Config
{ numRecords = 10
, numFields = 3
, inputFile = "/home/user1/project/indata.file"
, outputFile = "/home/user1/project/outdata.file"
, datefmt = "ddmmyyyy"
}
As an example, here is then the program that prints the value for "outputFile":
main :: IO ()
main = do
s <- readFile "config.txt"
let config = read s
putStrLn (outputFile config)