2012-05-28 37 views
1

我想教自己的Haskell。作为示例程序,我正在写一个蜘蛛纸牌游戏。使用getOpt解析Haskell的命令行参数

我想写一个命令行解析器使用System.Console.GetOpt。我知道有更简单的方法可以为此程序进行参数解析,但我想了解如何使用GetOpt模块,因为我预计稍后将在其他将要编写的程序中使用它的复杂性。

我想添加一个“ - 帮助”选项,只是打印一个用法消息,然后退出。如果“--games”选项或“--suits”选项的任一参数不是有效整数(游戏> = 1和< = 1000,适合== 1,2,或4)。我将把所产生的Options数据类型传递给我的程序的其他部分。

我也遇到了一个错误,progName不在范围内。是否case声明parseArgsdo块的范围内?

这里是我的代码,从例子中拼凑在"Real World Haskell"Haskell wiki

module Main (main) where 

import System.Console.GetOpt 
import System.Environment(getArgs, getProgName) 

data Options = Options { 
    optGames :: Int 
    , optSuits :: Int 
    , optVerbose :: Bool 
    } deriving Show 

defaultOptions = Options { 
    optGames = 1 
    , optSuits = 4 
    , optVerbose = False 
    } 

options :: [OptDescr (Options -> Options)] 
options = 
    [ Option ['g'] ["games"] 
     (ReqArg (\g opts -> opts { optGames = (read g) }) "GAMES") 
     "number of games" 
    , Option ['s'] ["suits"] 
     (ReqArg (\s opts -> opts { optSuits = (read s) }) "SUITS") 
     "number of suits" 
    , Option ['v'] ["verbose"] 
     (NoArg (\opts -> opts { optVerbose = True })) 
     "verbose output" 
    ] 

parseArgs :: IO Options 
parseArgs = do 
    argv <- getArgs 
    progName <- getProgName 
    case getOpt RequireOrder options argv of 
    (opts, [], []) -> return (foldl (flip id) defaultOptions opts) 
    (_, _, errs) -> ioError (userError (concat errs ++ helpMessage)) 
    where 
    header = "Usage: " ++ progName ++ " [OPTION...]" 
    helpMessage = usageInfo header options 

main :: IO() 
main = do 
    options <- parseArgs 
    putStrLn $ show options 
+5

范围的事情是因为where子句附加到'parseArgs',其中'progName'不在作用域中(它被绑定在do-block中)。如果你多缩进'where',它将附加到'case',并且'progName'在范围内。 –

+0

(如果除“为什么会出现此错误”之外还有其他问题,则应明确说明该问题。) –

+0

谢谢。我缩进了'where'语句,直到它比第二个case('(_,_,errs)')多一个缩进级别并且它能够工作,但是我认为值'header'和'helpMessage'不会是在第一个案件条款的范围内。我将它们移动到'do'块的'let'值上,而不是'where'。 – Ralph

回答

3

这里是我想出了解决方案:

module Main (main) where 

import Control.Monad 
import Control.Monad.Error 
import System.Console.GetOpt 
import System.Environment(getArgs, getProgName) 

data Options = Options { 
    optGames :: Int 
    , optSuits :: Int 
    , optVerbose :: Bool 
    } deriving Show 

defaultOptions = Options { 
    optGames = 1 
    , optSuits = 4 
    , optVerbose = False 
    } 

options :: [OptDescr (Options -> Either String Options)] 
options = 
    [ Option ['g'] ["games"] 
     (ReqArg (\g opts -> 
     case reads g of 
      [(games, "")] | games >= 1 && games <= 1000 -> Right opts { optGames = games } 
      _ -> Left "--games must be a number between 1 and 1000" 
     ) "GAMES") 
     "number of games" 
    , Option ['s'] ["suits"] 
     (ReqArg (\s opts -> 
     case reads s of 
      [(suits, "")] | suits `elem` [1, 2, 4] -> Right opts { optSuits = suits } 
      _ -> Left "--suits must be 1, 2, or 4" 
     ) "SUITS") 
     "number of suits" 
    , Option ['v'] ["verbose"] 
     (NoArg (\opts -> Right opts { optVerbose = True })) 
     "verbose output" 
    ] 

parseArgs :: IO Options 
parseArgs = do 
    argv <- getArgs 
    progName <- getProgName 
    let header = "Usage: " ++ progName ++ " [OPTION...]" 
    let helpMessage = usageInfo header options 
    case getOpt RequireOrder options argv of 
    (opts, [], []) -> 
     case foldM (flip id) defaultOptions opts of 
     Right opts -> return opts 
     Left errorMessage -> ioError (userError (errorMessage ++ "\n" ++ helpMessage)) 
    (_, _, errs) -> ioError (userError (concat errs ++ helpMessage)) 

main :: IO() 
main = do 
    options <- parseArgs 
    putStrLn $ show options 

我该如何提高呢?