我想在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
答案 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
]
如果您想知道Parser
和ParserInfo
的含义:通常我们会使用Parser
构建ParserInfo
,然后将其放入info
组合器用它来装饰它的附加信息(例如,使用progDesc
)。 Parser
可以由其他Parser
组成,通常使用应用组合器,但ParserInfo
只是Functor
,因为它代表程序的入口点。
由于每个命令都像一个小子程序,我们每个命令都需要ParserInfo
。 command
和subparser
组合器让我们可以使用ParserInfo
个Parser
并将它们包裹在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