4

私は Haskell を学んでいるので、純粋な部分を楽しんでいましたが、今はモナドと IO の部分につまずいており、言語について本当に腹立たしいと感じる人もいるでしょう。プロジェクトのオイラー問題を解決していますが、要素をインデックスで頻繁に更新する必要があるため、変更可能な配列が必要です。ベクトルを試してみましたが、動作させることができなかったので、Data.Array.IO を試しました。要素を読み書きすることはできますが、ターミナルで配列を希望どおりに表示できません。これまでのところ、私はこれを持っています。

test = do
    arr <- newArray (1,10) 37 :: IO (IOArray Int Int)
    a <- readArray arr 1
    writeArray arr 1 64
    b <- readArray arr 1
    dispArray arr 
    return ()

dispArray arr = do
    (a,b) <- getBounds arr
    printf "["
    dispArray' arr a
    printf "]\n"
        where dispArray' arr i = do
                (a,b) <- getBounds arr
                if i < a || i > b
                    then return ()
                    else do
                        v <- readArray arr i
                        print v
                        dispArray' arr (i+1)

あなたが期待するように、これの出力はこれです:

[64
37
37
37
37
37
37
37
37
37
]

でもこれじゃ不便だし、[64,37,37,37....こうして欲しい。のような関数を見てきましたがtoList、これは必要ありません。表示するたびにリストに変換したくありません。だから私は使用する必要があると考えましprintfた。だから私はに置き換えprint vましたprintf " %s," (show v)。しかし、これはコンパイルされません。どうしてか分かりません。文字列を意味するのでprint :: Show a => a -> IO ()show :: Show a => a -> Stringなぜ機能しないのでしょうか? %sそれで、私は隣同士に電話をかけました。printf が機能するかどうかを確認します。

printf " %s," "hello"
print v

コンパイルして表示するもの:

[ hello,64
 hello,37
 hello,37
 hello,37
 hello,37
 hello,37
 hello,37
 hello,37
 hello,37
 hello,37
]

なぜ使えないのshow vですか?haskell IO が初心者を怒らせるのはなぜですか?

4

2 に答える 2

6

これは興味深い型チェック パズルです。

printfへの呼び出しが生成するエラー メッセージは次のとおりです。

Could not deduce (PrintfType (m a0))
  arising from the ambiguity check for `dispArray'

フレーズCould not deduceambiguityは通常、GHC がこのプログラムをどのように型付けするべきかを結論付けるのに十分な型情報を持っていないという事実をほのめかしています。これは実際の型エラーである可能性がありますが、より多くの型情報を提供するだけで修正できる可能性もあります (ここではこれが当てはまります)。

ここでの犯人は実際printfには、変更可能な配列インターフェイスの柔軟性と組み合わされており、Haskell の IO システムではありません。のタイプprintfは巧妙なハックですが、それでもハックです。フォーマット文字列だけに依存するさまざまなタイプの柔軟な数のパラメーターを知るために、printfあまり安全でも有益でもないタイプがあります。

printf :: PrintfType r => String -> r

したがって、確かにわかっていることは、最初の引数が型であるということだけですString。残りrは、型クラスにある任意の型にすることができますPrintfType

インスタンスの詳細は重要ではありません。興味深いのは、 をshow生成することです。フォーマット文字列Stringに適用してから、-生成された2 番目の文字列を適用すると、まだかなり有益な型が残ります。printfshow

> :t printf "%s," (show 2)
printf "%s," (show 2) :: PrintfType t => t

特に、ここでは結果がIOモナドにあるという指示はありません。

これは、GHC がコンテキストからIO. しかし、 内dispArray'で呼び出す他の関数はreadArray, getBounds, return(およびdispArray'再帰的に) だけです。これらの関数はいずれも、どちらに存在するかを指定していませんIO。特に、すべての配列関数はモナド上でオーバーロードされます。例:

getBounds :: (Ix i, MArray a e m) => a i e -> m (i, i)

(実際、getBoundsたとえば、STモナド コンテキストでも動作する可能性があります。) したがってdispArray'、あなたが に住んでいることを決定するものは何もありませんIO。そしてそれは、GHC が の型を解決できないことを意味しprintfます。

printf私が言ったように、それprintf自体がこの情報を提供することができず、外部から利用可能でなければならないという望ましい柔軟性の結果です。

解決策は簡単です。コメントの 1 つで提案されているように、呼び出しの結果の型に注釈を付けるだけで十分printfです。

printf "%s," (show v) :: IO ()

とにかく使用しているprintfように(実際に10進数の配列にのみ関心がある場合)、次を使用することもできます:

printf "%d," v :: IO ()

dispArray'戻り値の型を に固定するために、 の定義内の他のものに型シグネチャを与えることも十分です (ただし、読者にはあまり明確ではありません) IO ()。たとえば、式return ()then-branch に注釈を付けることができます。if

return () :: IO ()
于 2013-04-03T08:39:36.507 に答える
6

あなたが望む呪文は次のとおりです。

putStr (show v)

vそれは改行なしで出力されます。

于 2013-04-03T02:10:34.863 に答える