如何使用Language.Haskell.Interpreter读取给定的配置文件并指定其中给出的值以初始化程序中的变量?
我的配置文件如下:
numRecords = 10
numFields = 3
inputFile = /home/user1/project/indata.file
outputFile = /home/user1/project/outdata.file
datefmt = ddmmyyyy
我想使用配置文件中给出的值初始化与配置文件中给出的标识符相对应的变量。
如何使用Language.Haskell.Interpreter来完成这件事? 我很困惑因为IO Monad和Interpreter Monad。这种小例子也很有用。
答案 0 :(得分:7)
为什么不呢?
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)
使用my.config
文件
Config {
size = 1024,
path = "/root/passwords.db"
}
使用ghci
*Main> main
Configured size := 1024
Configured path := "/root/passwords.db"
*Main>
(对不起以前的错误,我很着急)
答案 1 :(得分:4)
首先,您不能(至少以任何明显的方式)使用hint包中的Language.Haskell.Interpreter
来执行此操作。该模块中的函数用于读入和运行Haskell代码,而不是任意结构化数据。
对于读取结构化数据,您需要某种解析器。以下是您拥有的一些选项:
Read
类的自动派生实例。如果您需要读取的数据格式非常重要,或者如果解析失败时需要有用的错误消息,我建议您选择1.或2.并查阅相应工具的文档和库。请注意,您需要一些时间来熟悉其主要概念和界面。
如果您的数据格式足够简单(如您的示例中所示),并且如果您的愿望清单中的广泛错误报告不高,您可以轻松地滚动自己的解析器。
在您的示例中,配置文件本质上是键和值的列表,由换行符分隔。在Haskell中,我们可以通过字符串对列表来表示这样的列表:
type Config = [(String, String)]
“解析”一个配置然后简化为:(1)将输入字符串分成行,(2)将每行分成单词,(3)从每行中选择第一个和第三个单词:
readConfig :: String -> Config
readConfig s =
[(key, val) | line <- lines s, let (key : _ : val : _) = words line]
要从解析的配置文件中检索条目,我们可以使用函数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
此函数将第一个参数作为条目的键,并将第二个参数作为将原始值字符串转换为适当类型的函数。对于纯文本配置值,我们可以简单地将标识函数传递给get
:
inputFile, outputFile, datefmt :: Config -> String
inputFile = get "inputFile" id
outputFile = get "outputFile" id
datefmt = get "datefmt" id
对于整数条目,我们可以使用read
:
numRecords, numFields :: Config -> Int
numRecords = get "numRecords" read
numFields = get "numFields" read
也许这些模式很常见,可以被分解到他们自己的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"
作为示例,这里是读取配置文件并打印“outputFile”值的程序:
main :: IO ()
main = do
s <- readFile "config.txt"
let config = readConfig s
putStrLn (outputFile config)
如果您可以控制配置文件的格式,则可以引入用于保存配置数据的新数据类型,并让Haskell自动为其派生类Read
的实例。例如:
data Config = Config
{ numRecords :: Int
, numFields :: Int
, inputFile :: String
, outputFile :: String
, datefmt :: String
} deriving Read
现在,您需要确保配置文件与预期格式匹配。例如:
Config
{ numRecords = 10
, numFields = 3
, inputFile = "/home/user1/project/indata.file"
, outputFile = "/home/user1/project/outdata.file"
, datefmt = "ddmmyyyy"
}
例如,这里是打印“outputFile”值的程序:
main :: IO ()
main = do
s <- readFile "config.txt"
let config = read s
putStrLn (outputFile config)