2016-12-16 72 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

相關問題