这更多的是一个风格问题,而不是一个如何做的问题。
所以我有一个需要两个命令行参数的程序:一个字符串和一个整数。
我是这样实现的:
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
虽然这有效,但这是我第一次在 Haskell 中真正使用命令行参数,所以我不确定这是否是一种非常尴尬且难以理解的方式来完成我想要的事情。
使用惰性模式匹配是可行的,但我可以看到其他编码员可能会对它皱眉。使用读取来查看是否成功解析在编写它时肯定感觉很尴尬。
有更惯用的方法吗?
最佳答案
我建议使用 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
此处使用的防护中的绑定(bind)是 pattern guard ,Haskell 2010 中添加的一项新功能(以及之前常用的 GHC 扩展)。
像这样使用reads
是完全可以接受的;它基本上是从无效读取中正确恢复的唯一方法,至少在我们获得 readMaybe 或标准库中的类似内容之前(多年来一直有人建议这样做,但他们已经成为自行车脱落的牺牲品)。使用惰性模式匹配和条件来模拟 case
表达式是不太可接受的:)
另一种可能的替代方案,使用 view patterns扩展名是
case args of
[aString, reads -> [(n,_)]] ->
doStuffWith aString n
_ -> ...
这避免了一次性的aInteger
绑定(bind),并使“解析逻辑”接近参数列表的结构。然而,它不是标准的 Haskell(尽管扩展毫无争议)。
对于更复杂的参数处理,您可能需要研究专门的模块 - System.Console.GetOpt位于标准 base
库中,但仅处理选项(不处理参数解析),而 cmdlib和 cmdargs是更多“全栈”解决方案(尽管我警告您避免使用 cmdargs 的“隐式”模式,因为这是一种粗俗的不纯粹的黑客行为,使语法变得更好一些;但是,“显式”模式应该没问题)。
关于Haskell:解析命令行参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8610277/