我正在尝试一个非常简单的参数解析器this example。 第一个参数应该是一个double,第二个参数应该是一个整数,如果它们不是这两个类型,我想对else中指定的两个使用默认参数。这就是我所拥有的:
parseArgs :: [String] -> (Double, Integer)
parseArgs args = do
if length(args) == 2 then do
let v1 = read (args !! 0) :: Double
let v2 = read (args !! 1) :: Integer
return (v1, v2)
else do
let v1 = read ("0.5") :: Double
let v2 = read ("5") :: Integer
return (v1, v2)
我知道有更复杂的方法可以使用Applicative
使用由optparse启发的设计来解析参数,但我还没有。
这是我得到的错误:
myscript.hs:186:5-31: error: …
• Couldn't match type ‘(Double, Integer)’ with ‘Integer’
Expected type: (Double, Integer)
Actual type: (Double, (Double, Integer))
• In a stmt of a 'do' block: return (v1, v2)
我不明白这一点。看一下getArgs
的签名,我没有看到任何奇怪的东西,表明我无法恢复我的int,或者它应该返回(Double,Integer)
而不仅仅是{{1} }}
我该如何正确地做到这一点?
答案 0 :(得分:6)
您不需要在此处使用do
表示法,因为这是为了处理某些monadic类型。您只需匹配输入列表:
parseArgs :: [String] -> (Double, Integer)
parseArgs [d, i] = (read d, read i)
parseArgs _ = (0.5, 5)
答案 1 :(得分:5)
在我看来,你在" 命令式"中有一些经验。 return
似乎是从函数返回内容的关键字的世界。
然而,在Haskell中,return
语句用于定义/使用monad。顺便提一下do
块也一样。您的类型(Int,Double)
可以用作monadic类型(为@duplode提供赞誉)。但在这种情况下,它看起来并不像你想要/必须使用monad,因为这看起来像一个简单的函数。
所以你可以用以下方法解决问题:
parseArgs :: [String] -> (Double, Integer)
parseArgs args =
if length(args) == 2 then
let v1 = read (args !! 0) :: Double
v2 = read (args !! 1) :: Integer
in (v1, v2)
else
let v1 = read ("0.5") :: Double
v2 = read ("5") :: Integer
in (v1, v2)
因此,您使用in
表示您在表达式中使用v1
和v2
等。它仍然不是很好的#Haskell-ish"。更好的方法是使用警卫(放下if-else
):
parseArgs :: [String] -> (Double, Integer)
parseArgs args | length args == 2 =
let v1 = read (args !! 0) :: Double
v2 = read (args !! 1) :: Integer
in (v1, v2)
| otherwise =
let v1 = read ("0.5") :: Double
v2 = read ("5") :: Integer
in (v1, v2)
最后,我真的不明白你为什么要使用所有这些let
语句并指定类型。您只需将其重写为:
parseArgs :: [String] -> (Double, Integer)
parseArgs args | length args == 2 = (read (args !! 0), read (args !! 1))
| otherwise = (read "0.5", read "5")
现在我们还没有完成。由于args
的长度为2,因此它的形状为[a,b]
。我们可以在头脑中使用这种模式:
parseArgs :: [String] -> (Double, Integer)
parseArgs [a,b] = (read a, read b)
parseArgs _ = (read "0.5", read "5")
优势在于您不再需要使用(!!)
来获取 i -th元素:检查和匹配可以同时完成。
我建议的最后一项改进是在第二种情况下省略read
:您只需输入0.5
和5
:
parseArgs :: [String] -> (Double, Integer)
parseArgs [a,b] = (read a, read b)
parseArgs _ = (0.5, 5)