2

在过去的几天里,我一直在尝试学习 Haskell。虽然我正在慢慢好转,但我发现很难用 Haskell 的 IO 推理,可能是因为我缺乏知识。我一直在尝试编写一个简单的待办事项列表程序。这是我所拥有的:

tadd todo = do
    td <- getLine
    td:todo

tdel todo = do
    trem <- getLine
    let rid = read trem :: Int
    [todo !! x | x <- [0..(length todo-1)], not $ x == rid]

tls todo = do
    mapM putStrLn [ (show x) ++ (todo !! x) | x <- [0..(length todo -1)] ]
    todo

mtodo "add" todo = tadd todo
mtodo "del" todo = tdel todo
mtodo "ls"  todo = tls todo


bege = do
    com <- getLine
    mtodo com []

main = bege

我排除了 mtodo to bemtodo :: [IO String] -> [IO String] -> [IO String] 和 tadd, tdel, tls to be :: [IO String] -> [IO String]

相反,我只是收到这个可怕的错误消息

[1 of 1] Compiling Main             ( todo.hs, todo.o )

todo.hs:3:9:
    Couldn't match type `[]' with `IO'
    Expected type: IO String
      Actual type: [String]
    In a stmt of a 'do' block: td : todo
    In the expression:
      do { td <- getLine;
           td : todo }
    In an equation for `tadd':
        tadd todo
          = do { td <- getLine;
                 td : todo }

todo.hs:8:9:
    Couldn't match expected type `IO' with actual type `[]'
    In a stmt of a 'do' block:
      [todo !! x | x <- [0 .. (length todo - 1)], not $ x == rid]
    In the expression:
      do { trem <- getLine;
           let rid = ...;
           [todo !! x | x <- [0 .. (length todo - 1)], not $ x == rid] }
    In an equation for `tdel':
        tdel todo
          = do { trem <- getLine;
                 let rid = ...;
                 [todo !! x | x <- [0 .. (length todo - 1)], not $ x == rid] }

todo.hs:12:9:
    Couldn't match type `[]' with `IO'
    Expected type: IO [Char]
      Actual type: [[Char]]
    In a stmt of a 'do' block: todo
    In the expression:
      do { mapM
             putStrLn [(show x) ++ (todo !! x) | x <- [0 .. (length todo - 1)]];
           todo }
    In an equation for `tls':
        tls todo
          = do { mapM
                   putStrLn [(show x) ++ (todo !! x) | x <- [0 .. (length todo - 1)]];
                 todo }

任何想法我的类型有什么问题?(另外 - 有什么我应该改变的吗?)。谢谢

4

2 回答 2

10

看这段代码:

tadd todo = do
    td <- getLine
    td:todo

问题是你自己有td:todo一条线;你正在使用getLine,所以整个块应该使用IO,但你的最后一行是一个列表。这就是“无法匹配”错误的含义——代码似乎是这样说的,IO应该[]是同一件事,但当然不是。

要将一个值提升到 monadic 上下文中以将其用作do块的结果值,请使用以下函数return

tadd todo = do
    td <- getLine
    return $ td:todo

这将给出tadd类型[String] -> IO [String]。这不是您似乎期望的类型,但是从您所写的内容来看,我认为您不想使用[IO String].

这同样适用于 in 中的列表理解tdel和最终todoin tls

此外,您在此处的预期类型已关闭:

mtodo :: [IO String] -> [IO String] -> [IO String]
mtodo "add" todo = tadd todo
mtodo "del" todo = tdel todo
mtodo "ls"  todo = tls todo

第一个参数显然是 a Stringhere,因此基于上述您可能想要mtodo :: String -> [String] -> IO [String]

这可能也不是您想要的:

bege = do
    com <- getLine
    mtodo com []

其他一些注意事项:

根据其余代码,您似乎想要一个交互式程序,但这只会运行一次。你可能想要的是这样的:

bege todo = do
    com <- getLine
    todo' <- mtodo com todo
    bege todo'

main = bege []

您的列表理解[todo !! x | x <- [0..(length todo-1)], not $ x == rid]效率非常低,而且根本不是惯用的。作为一个经验法则,如果你还在学习 Haskell,你可能永远不应该使用(!!),除非你后来放弃了这个列表。Haskell 列表是线性序列,因此索引需要遍历所有内容。对于你正在做的事情来说,这并不是最好的数据结构,但你至少应该找到一些不需要多次遍历列表的方法。

作为避免的示例(!!),您可以重写mapM putStrLn [ (show x) ++ (todo !! x) | x <- [0..(length todo -1)] ]as mapM_ putStrLn $ zipWith (\n t -> show n ++ t) [0..] todo,这样可以避免所有令人困惑和多余的东西,例如索引和担心列表的长度,以及只遍历列表一次,而不是先遍历它来计算长度,然后部分遍历每个项目以打印它。

这更像是一个品味问题,而不是这样写:

mtodo "add" todo = tadd todo
mtodo "del" todo = tdel todo
mtodo "ls"  todo = tls todo

您可以将其缩写为:

mtodo "add" = tadd
mtodo "del" = tdel
mtodo "ls"  = tls

...这意味着完全相同的事情,并且可以通过减少多余的噪音并使其明显地表明它所做的只是根据给定的字符串选择其他几个函数之一来更清晰。

此外,该let rid = read trem :: Int位是不必要的 - 只需使用函数readLn而不是getLine,这正是您想要的。此外,类型注释可能是不必要的(并且会分散注意力),因为应该根据您对结果的处理来推断类型。

于 2013-02-14T15:10:56.723 回答
1

您需要了解退货。编译它所需要做的就是将 return 添加到 tadd、tdel 和 tls 的最后一行。

如果您使用显式类型签名,您将收到更好的错误消息。

tadd :: [String] -> IO [String]
tadd todo = do
  td <- getLine
  return (td:todo)

在这种情况下,(td:todo)有 type [String],您需要添加 return 将其带入 IO monad,这就是函数需要返回的内容。当您使用 do 表示法时,每一行都需要在您所在的 monad 中有一个类型(以 let 开头的除外),在本例中为 IO。

tdel 和 tls 有完全相同的问题。

tdel :: [String] -> IO [String]
tdel todo = do
  trem <- getLine
  let rid = read trem :: Int
  return [todo !! x | x <- [0..(length todo-1)], not $ x == rid]

tls :: [String] -> IO [String]
tls todo = do
  mapM putStrLn [ (show x) ++ (todo !! x) | x <- [0..(length todo -1)] ]
  return todo
于 2013-02-14T15:13:34.697 回答