使用Haskell中的命令行参数调度以更正函数

Jos*_*sto 1 io monads haskell

我在Haskell写了一个小命令行程序.我需要它根据命令行参数调度到正确的加密函数.我已经走了那么远,但后来我需要剩下的参数作为参数传递给函数.我读了:

http://learnyouahaskell.com/input-and-output

这让我走到了这一步:

import qualified CaesarCiphers
import qualified ExptCiphers

dispatch::[(String, String->IO ())]
dispatch = [("EEncipher", ExptCiphers.exptEncipherString)
            ("EDecipher", ExptCiphers.exptDecipherString)
            ("CEncipher", CaesarCiphers.caesarEncipherString)
            ("CDecipher", CaesarCiphers.caesarDecipherString)
            ("CBruteForce", CaesarCiphers.bruteForceCaesar)]

main = do
    (command:args) <- getArgs
Run Code Online (Sandbox Code Playgroud)

每个函数都需要一些我不知道的参数,直到运行时.如何将它们传递给函数,因为它们将被绑定在列表中?我只是手动抓住它们吗?喜欢:

exampleFunction (args !! 1) (args !! 2) 
Run Code Online (Sandbox Code Playgroud)

这看起来有点难看.是否有某种惯用的方法来做到这一点?那么错误检查呢?我的功能无法正常处理错误,例如以愚蠢的顺序获取传递的参数.

另外,重要的是,dispatch中的每个函数都使用不同数量的参数,所以我无论如何都不能静态地执行此操作(如上所述.)太糟糕了,unCurry command argsHaskell无效.

dav*_*420 7

一种方法是将函数包装在进一步执行命令行处理的函数中.例如

dispatch::[(String, [String]->IO ())]
dispatch = [("EEncipher", takesSingleArg ExptCiphers.exptEncipherString)
            ("EDecipher", takesSingleArg ExptCiphers.exptDecipherString)
            ("CEncipher", takesTwoArgs CaesarCiphers.caesarEncipherString)
            ("CDecipher", takesTwoArgs CaesarCiphers.caesarDecipherString)
            ("CBruteForce", takesSingleArg CaesarCiphers.bruteForceCaesar)]

-- a couple of wrapper functions:

takesSingleArg :: (String -> IO ()) -> [String] -> IO ()
takesSingleArg act [arg] = act arg
takesSingleArg _   _     = showUsageMessage

takesTwoArgs :: (String -> String -> IO ()) -> [String] -> IO ()
takesTwoArgs act [arg1, arg2] = act arg1 arg2
takesTwoArgs _   _            = showUsageMessage

-- put it all together

main = do
    (command:args) <- getArgs
    case lookup command dispatch of
         Just act -> act args
         Nothing  -> showUsageMessage
Run Code Online (Sandbox Code Playgroud)

您可以通过使包装函数的变体执行错误检查来扩展它,并根据需要将其参数转换为(/某些)Ints/custom数据类型/ etc.

正如dbaupp所说,我们getArgs上面模式匹配的方式并不安全.更好的方法是

run :: [String] -> IO ()
run [] = showUsageMessage
run (command : args)
   = case lookup command dispatch of
          Just act -> act args
          Nothing  -> showUsageMessage

main = run =<< getArgs
Run Code Online (Sandbox Code Playgroud)

  • 应该避免`getArgs`上的模式匹配:如果没有参数,失败的匹配将生成一个无法捕获的异常.最好在调度之前捕获`getArgs`的整个输出并使用`null`进行测试. (2认同)