2

ここ数日、私は Haskell を学ぼうとしてきました。私はゆっくりと良くなっていますが、おそらく私の知識不足のために、Haskell の IO を推論するのは難しいと感じています。簡単な todo リスト プログラムを作成しようとしています。これが私が持っているものです:

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 が bemtodo :: [IO String] -> [IO String] -> [IO String] で、tadd, tdel, tls が 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[]同じはずですが、もちろんそうではありません。

doブロックの結果値として使用するために値をモナド コンテキストに持ち上げるには、次の関数を使用しますreturn

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

これによりtadd、タイプが得られ[String] -> IO [String]ます。これはあなたがタイプに期待しているようには見えませんが、あなたが書いたことから、あなたは[IO String].

同じことが のリスト内包表記tdelと の最終todoのものにも当てはまりますtls

また、ここで期待されるタイプはオフです:

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

最初の引数は明らかにStringここにあるため、上記に基づいて、おそらく必要です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 のリストは線形シーケンスであるため、インデックスを作成するには、その時点までのすべてをトラバースする必要があります。あなたがやっていることにとって本当に最適なデータ構造ではありませんが、少なくともリストを何度もトラバースする必要がない方法を見つける必要があります。

を回避する例として、次のように(!!)書き直すことができます。これにより、最初にリストをトラバースして長さを計算する代わりに、インデックス作成やリストの長さの心配、リストを 1 回だけトラバースするなどの紛らわしくて余分なものをすべて回避できます。各アイテムまで部分的にトラバースして印刷します。mapM putStrLn [ (show x) ++ (todo !! x) | x <- [0..(length todo -1)] ]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

リターンについて学ぶ必要があります。これをコンパイルするには、tadd、tdel、および tls の最後の行に return を追加するだけです。

明示的な型シグネチャを使用すると、より適切なエラー メッセージが表示されます。

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

この場合、(td:todo)type[String]を持っているので、それを IO モナドに入れるために return を追加する必要があります。これは、関数が返す必要があるものです。do 記法を使用する場合、各行は現在のモナドの型を持つ必要があります (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 に答える