2016-12-16 75 views
4

我刚刚发现我精心打造的解析器无法解析我扔在任何字符串:是否可以通过多个参数进行optparse-applicative选项?

roi :: Parser (Maybe ROI) 
roi = optional $ option (ROI <$> auto <*> auto <*> auto <*> auto) 
       $ long "roi" <> metavar "ROI" <> help "Only process selected region of interest" 

其中ROI = ROI Int Int Int Int

如果这是很重要的,它是嵌套在一个更高的解析器

options :: Parser Opts 
options = Opts <$> input <*> output <*> roi <*> startT <*> endT 

其中Opts是一个合适的ADT。

现在我假设roi解析器将解析表达式,如--roi 1 2 3 4,但它失败,并且给我使用消息Invalid argument '128'

--roi 1而不是解析,但返回Just (ROI 1 1 1 1)

有没有一种方法,使这项工作?

+0

我已经编辑我的回答澄清'--roi 1 2 3 4'不会与任何这里的方法的工作 - 最接近你可以得到像'--roi 1,2,3,4'这样的东西,就像在Cubic的答案中一样。 – duplode

回答

6

我不认为选项应该消耗多个参数。至少我不确定你会怎样去实现它。我建议你简单地放弃这个想法,然后把你的ROI选项放到一个参数中,使用类似--roi 1,2,3,4的语法。

你会只需要实施一个定制阅读器,这里是你如何能做到这一点的例子:

module Main where 

import Options.Applicative 

data ROI = ROI Int Int Int Int 
    deriving Show 

-- didn't remember what this function was called, don't use this 
splitOn :: Eq a => a -> [a] -> [[a]] 
splitOn sep (x:xs) | sep==x  = [] : splitOn sep xs 
        | otherwise = let (xs':xss) = splitOn sep xs in (x:xs'):xss 
splitOn _ [] = [[]] 

roiReader :: ReadM ROI 
roiReader = do 
    o <- str 
    -- no error checking, don't actually do this 
    let [a,b,c,d] = map read $ splitOn ',' o 
    return $ ROI a b c d 

roiParser :: Parser ROI 
roiParser = option roiReader (long "roi") 

main :: IO() 
main = execParser opts >>= print where 
    opts = info (helper <*> roiParser) fullDesc 
+0

为什么不能用''''作为分隔符而不是'',''?这样你可以解析'roi 1 2 3 4' – jkeuhlen

+0

@jkeuhlen因为如果你尝试从shell中使用它,你必须引用参数:'hello --roi“1 2 3 4”',它只是使它更难使用,所以有点错过了;) – Cubic

+0

啊谢谢你我错过了! – jkeuhlen

5

类型的option是:

​​

ReadM,在转向,是“ReaderT字符串以外的新类型”,由选项阅读器使用“。由于option使用ReaderT引擎盖下,当你的ReadM像你这样在这里Applicative情况下使用它...

ROI <$> auto <*> auto <*> auto <*> auto 

...相同,整体而言,输入字符串被提供给每个四个auto解析器,因为这就是读者/函数应用实例的工作方式。

如果您希望将由空格分隔的值分解为单个ROI,则需要编写自定义分析程序。这是一个不太特别的尝试,围绕eitherReader。请注意,这将要求值位于引号(--roi "1 2 3 4")内,以便将它们作为单个字符串引入。 Cubic的答案提出了一种替代方法,它使用逗号分隔值(--roi 1,2,3,4)。

import Text.Read (readEither) 

-- etc. 

roi :: Parser (Maybe ROI) 
roi = optional 
    $ option (eitherReader $ \inp -> case traverse readEither (words inp) of 
     Right [x, y, z, w] -> Right (ROI x y z w) 
     Right _ -> Left "ROI requires exactly 4 values" 
     Left _ -> Left "ROI requires integer values") 
    $ long "roi" <> metavar "ROI" <> help "Only process selected region of interest" 

成功和失败的模式:

GHCi> execParserPure defaultPrefs (info roi mempty) ["--roi","1 2 3 4"] 
Success (Just (ROI 1 2 3 4)) 
GHCi> execParserPure defaultPrefs (info roi mempty) ["--roi","1 2 3"] 
Failure (ParserFailure (option --roi: ROI requires exactly 4 values 

Usage: <program> [--roi ROI],ExitFailure 1,80)) 
GHCi> execParserPure defaultPrefs (info roi mempty) ["--roi","1 2 foo 4"] 
Failure (ParserFailure (option --roi: ROI requires integer values 

Usage: <program> [--roi ROI],ExitFailure 1,80)) 
+2

请注意,这会读取与“--roi 1 2 3 4”不同的命令行选项'--roi“1 2 3 4”'。没有一个理智的optparse-appative解析器来解析'--roi 1 2 3 4'; optparse-appative'option'“是一个选项,它接受一个* single *参数,解析它并返回一个值。” – Cirdec

+0

@Cirdec澄清添加到答案;谢谢你强调这一点。 – duplode

+0

是的......这工作(我正在做一个类似的解析为四元组)...但我觉得@Cubics答案是“更真实”。 – fho

相关问题