ram*_*ion 27 haskell command-line-arguments
这更像是一个风格问题,而不是一个如何.
所以我有一个需要两个命令行参数的程序:一个字符串和一个整数.
我这样实现了它:
main = do
args@(~( aString : aInteger : [] ) ) <- getArgs
let parsed@( ~[(n,_)] ) = reads aInteger
if length args /= 2 || L.null parsed
then do
name <- getProgName
hPutStrLn stderr $ "usage: " ++ name ++ " <string> <integer>"
exitFailure
else do
doStuffWith aString n
Run Code Online (Sandbox Code Playgroud)
虽然这有效,但这是我第一次真正在Haskell中使用命令行参数,所以我不确定这是否是一种非常尴尬和难以理解的方式来做我想要的.
使用惰性模式匹配工作,但我可以看到它是如何被其他编码器所厌恶.并且使用read来查看我是否有一个成功的解析在写它时肯定感到尴尬.
有没有比较惯用的方法呢?
ehi*_*ird 20
我建议使用一个case表达式:
main :: IO ()
main = do
args <- getArgs
case args of
[aString, aInteger] | [(n,_)] <- reads aInteger ->
doStuffWith aString n
_ -> do
name <- getProgName
hPutStrLn stderr $ "usage: " ++ name ++ " <string> <integer>"
exitFailure
Run Code Online (Sandbox Code Playgroud)
这里使用的防护装置中的绑定是模式防护,是Haskell 2010中添加的新功能(以及之前常用的GHC扩展).
这样使用reads是完全可以接受的; 它基本上是从无效读取中正确恢复的唯一方法,至少直到我们readMaybe在标准库中获得或类似的东西(多年来一直有提议,但它们已经成为了自行车的牺牲品).使用延迟模式匹配和条件来模拟case表达式是不太可接受的:)
使用视图模式扩展的另一种可能的替代方案是
case args of
[aString, reads -> [(n,_)]] ->
doStuffWith aString n
_ -> ...
Run Code Online (Sandbox Code Playgroud)
这避免了一次性aInteger绑定,并使"解析逻辑"保持接近参数列表的结构.然而,它不是标准的Haskell(虽然扩展绝不是有争议的).
对于更复杂的参数处理,您可能需要查看专用模块 - System.Console.GetOpt在标准base库中,但只处理选项(不是参数解析),而cmdlib和cmdargs是更"全栈"解决方案(虽然我提醒你要避免cmdargs的"隐式"模式,因为这是一个非常不纯的黑客,使语法更好一些;但"显式"模式应该没问题.
Sté*_*ent 11
我同意这个optparse-applicative包非常好.真棒!让我给出一个最新的例子.
该程序将字符串和整数n作为参数,返回复制n次的字符串,并且它有一个反转字符串的标志.
-- file: repstring.hs
import Options.Applicative
import Data.Monoid ((<>))
data Sample = Sample
{ string :: String
, n :: Int
, flip :: Bool }
replicateString :: Sample -> IO ()
replicateString (Sample string n flip) =
do
if not flip then putStrLn repstring else putStrLn $ reverse repstring
where repstring = foldr (++) "" $ replicate n string
sample :: Parser Sample
sample = Sample
<$> argument str
( metavar "STRING"
<> help "String to replicate" )
<*> argument auto
( metavar "INTEGER"
<> help "Number of replicates" )
<*> switch
( long "flip"
<> short 'f'
<> help "Whether to reverse the string" )
main :: IO ()
main = execParser opts >>= replicateString
where
opts = info (helper <*> sample)
( fullDesc
<> progDesc "Replicate a string"
<> header "repstring - an example of the optparse-applicative package" )
Run Code Online (Sandbox Code Playgroud)
编译文件后(ghc像往常一样):
$ ./repstring --help
repstring - an example of the optparse-applicative package
Usage: repstring STRING INTEGER [-f|--flip]
Replicate a string
Available options:
-h,--help Show this help text
STRING String to replicate
INTEGER Number of replicates
-f,--flip Whether to reverse the string
$ ./repstring "hi" 3
hihihi
$ ./repstring "hi" 3 -f
ihihih
Run Code Online (Sandbox Code Playgroud)
现在,假设您需要一个可选参数,一个要附加在字符串末尾的名称:
-- file: repstring2.hs
import Options.Applicative
import Data.Monoid ((<>))
import Data.Maybe (fromJust, isJust)
data Sample = Sample
{ string :: String
, n :: Int
, flip :: Bool
, name :: Maybe String }
replicateString :: Sample -> IO ()
replicateString (Sample string n flip maybeName) =
do
if not flip then putStrLn $ repstring ++ name else putStrLn $ reverse repstring ++ name
where repstring = foldr (++) "" $ replicate n string
name = if isJust maybeName then fromJust maybeName else ""
sample :: Parser Sample
sample = Sample
<$> argument str
( metavar "STRING"
<> help "String to replicate" )
<*> argument auto
( metavar "INTEGER"
<> help "Number of replicates" )
<*> switch
( long "flip"
<> short 'f'
<> help "Whether to reverse the string" )
<*> ( optional $ strOption
( metavar "NAME"
<> long "append"
<> short 'a'
<> help "Append name" ))
Run Code Online (Sandbox Code Playgroud)
编译并享受乐趣:
$ ./repstring2 "hi" 3 -f -a rampion
ihihihrampion
Run Code Online (Sandbox Code Playgroud)
Haskell 中有很多参数/选项解析库,它们比使用read/更容易getOpt,现代的示例(optparse-applicative)可能会引起人们的兴趣:
import Options.Applicative
doStuffWith :: String -> Int -> IO ()
doStuffWith s n = mapM_ putStrLn $ replicate n s
parser = fmap (,)
(argument str (metavar "<string>")) <*>
(argument auto (metavar "<integer>"))
main = execParser (info parser fullDesc) >>= (uncurry doStuffWith)
Run Code Online (Sandbox Code Playgroud)
这些天,我非常喜欢使用optparse-generic来解析命令行参数:
随着您的程序逐渐成熟,您可能想要提供一个完整的帮助,以及一个很好的注释选项数据类型,这options-generic是非常棒的。但它在解析列表和元组方面也很出色,根本没有任何注释,因此您可以立即开始运行:
例如
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Options.Generic
main :: IO ()
main = do
(n, c) <- getRecord "Example program"
putStrLn $ replicate n c
Run Code Online (Sandbox Code Playgroud)
运行如下:
$ ./OptparseGenericExample
Missing: INT CHAR
Usage: OptparseGenericExample INT CHAR
$ ./OptparseGenericExample 5 c
ccccc
Run Code Online (Sandbox Code Playgroud)