Haskell:解析命令行参数

时间:2014-11-15 08:48:06

标签: haskell

我想在Haskell中编写一个程序,它将接受命令行参数。例如:要打印系列的前6个元素的总和(将由另一个函数计算),我将写:

sum 6

应显示正确的答案。我必须通过检查命令行为另外5-7个不同的命令执行此操作。我该怎么办?切换机箱是个好主意吗?如果是这样,任何人都可以告诉我如何做到这一点。

SOLUTION:

main = do
--Get some input
    f <- getLine
--Split the input into 2 strings; one is COMMAND field and other is the ARGUMENT field using the condition the there is one space between them   
    let cmd = takeWhile (/=' ') f
    let arg = dropWhile (/=' ') f


    let val = tail arg  
    let p = read val::Int


--Check for the COMMAND     
    case cmd of
        "SUM" -> if (empty arg) then do { putStrLn "ERR"; exitWith ExitSuccess} else if (check val) then print (sum1 p) else do { putStrLn "ERR"; exitWith ExitSuccess}
        "NTH" -> if (empty arg) then do { putStrLn "ERR"; exitWith ExitSuccess} else if (check val) then print (fact p) else do { putStrLn "ERR"; exitWith ExitSuccess}
        "BOUNDS" -> if (empty arg) then do { putStrLn "ERR"; exitWith ExitSuccess} else if (check val == False) then do { putStrLn "ERR"; exitWith ExitSuccess} else if (p > 1) then do { print c; print d} else do { putStrLn"ERR"; exitWith ExitSuccess}  
        "QUIT" -> if (empty arg) then exitWith ExitSuccess else do { putStrLn "ERR"; exitWith ExitSuccess}
            _ -> do { putStrLn "ERR"; exitWith ExitSuccess}

--Repeat main until QUIT
    main

3 个答案:

答案 0 :(得分:6)

optparse-applicative是支持这种子命令解析的库的一个例子。

假设您的程序现在有两个命令,“sum”和“mean”。我们可以使用代数数据类型来表示命令及其参数,这里称为Command

import Data.Monoid (mconcat)
import Options.Applicative

data Command = Sum Integer
             | Mean Integer
               -- et cetera

我们可以构建一个接受所有命令的解析器,为每个命令编写解析器,然后编写它们。

parseNumber :: Parser Integer
parseNumber = argument auto (metavar "N")

sumParser :: ParserInfo Command
sumParser = info (Sum <$> parseNumber)
                 (progDesc "Sum first N elements in series")

meanParser :: ParserInfo Command
meanParser = info (Mean <$> parseNumber)
                  (progDesc "Mean of first N elements in series")

commandParser :: ParserInfo Command
commandParser = info commands $ progDesc "My program" where
    commands = subparser $ mconcat [
          command "sum" sumParser
        , command "mean" meanParser
        ]

如果您想知道ParserParserInfo的含义:通常我们会使用Parser构建ParserInfo,然后将其放入info组合器用它来装饰它的附加信息(例如,使用progDesc)。 Parser可以由其他Parser组成,通常使用应用组合器,但ParserInfo只是Functor,因为它代表程序的入口点。

由于每个命令都像一个小子程序,我们每个命令都需要ParserInfocommandsubparser组合器让我们可以使用ParserInfoParser并将它们包裹在main :: IO () main = do cmd <- execParser commandParser case cmd of Sum n -> return () -- TODO perform sum command Mean n -> return () -- TODO perform mean command 中,将多个入口点合并为一个。

一旦我们得到了解析器的结果,我们就可以通过对结果进行模式匹配来调度到适当的例程。

{{1}}

答案 1 :(得分:4)

当然,如果你有时间和需要,使用命令行解析器库比切换案例要好得多。正确的解析器使您能够以任何顺序拥有标记,自动记录等...虽然如果您现在不需要任何标记,您可能稍后需要它。

但是,模式匹配允许您检查列表中的值,但是虽然列表的大小,但同时。这使得在Haskell中编写穷人命令行解析变得简单。

实施例

main = do
     args <- getArg
     case args of
        ["command1", a, b] -> command1 a b -- 2 argument
        ["command2", a ]   -> command2 a -- 1 argument
        "command3":as      -> command3 as  -- n arguments
        otherwise          -> putStrLn "Please read the code to see which arguments are acceptable :-)"

所以即使我建议使用解析库,如果你只需要几个没有标志的选项,并且没有时间学习/选择一个,那么一个简单的case ... of就相当简洁了更快/更简单的写作。

答案 2 :(得分:0)

您可以在几行中编写自己的简单应用程序样式解析器。这个想法是:接受一个字符串对列表,其中第一个字符串是一个选项名称,第二个字符串是一个选项值,查找当前选项名称,如果找到它,以某种方式处理相关值从列表中删除该对。如果找不到,请返回Nothing。所以Parser定义如下:

type Parser = StateT [(String, String)] Maybe

这是主要功能:

option :: (String -> Maybe a) -> String -> Parser a
option f str = StateT $ \xs -> do
        (v, xs') <- lookupDelete str xs
        v' <- f v
        return (v', xs')

lookupDelete所说的内容。实际选项解析器是:

sopt :: String -> Parser String
sopt = option Just

opt :: Read a => String -> Parser a
opt = option $ reads >>> listToMaybe >=> finish

finish (x, []) = Just x
finish  _      = Nothing

opt解析器尝试读取字符串,如果完全读取该字符串,则成功。

optPairs  []                     = Just []
optPairs (('-':'-':name):opt:xs) = ((name, opt) :) <$> optPairs xs
optPairs  _                      = Nothing

此功能将输入分成对。最后

parse :: Parser a -> String -> Maybe a
parse p = words >>> optPairs >=> runStateT p >=> finish

以下是一个例子:

data SubCommand = SubCommand String (Double, Double)
                deriving (Show)

data Command = Sum [Integer]
             | Sub SubCommand
             deriving (Show)

subcommandParser :: Parser SubCommand
subcommandParser = SubCommand <$> sopt "str" <*> opt "dbls"

commandParser :: Parser Command
commandParser = Sum <$> opt "sum" <|> Sub <$> subcommandParser

main = mapM_ (print . parse commandParser)
    [ "--sum [1,2,3,4]"
    , "--str option --dbls (2.2,0)"
    , "--dbls (2.2,0) --str option"
    , "--smth smth"
    ]

结果

Just (Sum [1,2,3,4])
Just (Sub (SubCommand "option" (2.2,0.0)))
Just (Sub (SubCommand "option" (2.2,0.0)))
Nothing

整个代码:http://lpaste.net/114365