2

我有一个程序需要一些命令行参数。

假设第一个命令行参数是一个逗号分隔值 (CSV) 整数列表。

我想将第一个参数转换"1,2,4,8,16"[1,2,4,8,16]. 我试图将字符串解析为Int列表,但出现编译错误。

哈斯克尔代码:

import System.Environment
import Data.List
import Text.Regex

main = do
  args <- getArgs

  ints <- if   (length args > 1)
          then (mapM read (splitRegex (mkRegex ",") (args!!1)))
          else [1,3,5] -- defaults
  print (ints)

编译错误:

myProg.hs:10:16:
    Couldn't match expected type `IO' with actual type `[]'
    In the expression: [1, 3, 5]
    In a stmt of a 'do' block:
      ints <- if (length args > 1) then
                  (mapM read (splitRegex (mkRegex ",") (args !! 1)))
              else
                  [1, 3, 5]
    In the expression:
      do { args <- getArgs;
           ints <- if (length args > 1) then
                       (mapM read (splitRegex (mkRegex ",") (args !! 1)))
                   else
                       [1, ....];
           print (ints) }

我不确定这种类型的错误是什么意思。如果有人可以向我解释类型错误以及如何修改我的代码以达到预期的结果,我将不胜感激。

4

2 回答 2

3

您不想使用<-定义ints,因为您没有在其中执行 IO 操作。您可以只使用let绑定。这也让您可以mapM用普通的map.

第一个正确的参数也被索引为 0,而不是像您在 C 中看到的那样为 1。您也可以使用它head来获取它。

let ints = if   length args >= 1
           then map read (splitRegex (mkRegex ",") (head args))
           else [1, 3, 5]
于 2013-11-09T20:18:22.877 回答
2

表达方式

if   (length args > 1)
then (mapM read (splitRegex (mkRegex ",") (args!!1)))
else [1,3,5] -- defaults

是错误类型的,因为这两种情况(然后,否则)的值不重合。mapM 的类型是

mapM :: Monad m => (a -> m b) -> [a] -> m [b]

因此对于某些 monad m(在本例中为 IO), then 分支的类型为 m [b]。然而,else 分支只是一个数字列表。您可以通过编写来修复错误

return [1,3,5]

对于 else 情况,使其类型为 IO [Int]。

但这可能不是最好的前进方式。您的 then 分支有问题;read 函数在 IO 中没有返回值,所以这里不适合作为 mapM 的第一个参数。实际上,没有理由在 IO 中进行这种计算,因为 int 的值是作为参数 (args) 的纯函数获得的。我建议在 main 之外实现它,作为具有类型的函数

extractInts :: [String] -> [Int]

然后,您可以像这样将其放入 main 中:

main = do
  args <- getArgs
  let ints = extractInts args
  print ints
于 2013-11-09T20:31:01.587 回答