私はこの質問をできるだけ短くそして要点を述べようとします。
コマンドラインに3文字入力すると、この計算が完了すると思いました。ただし、そうではありません。なぜそうなのか気になります。
return . take 3 =<< sequence (repeat getChar)
編集:
選択を世代から分離しようとしているので、選択した文字の無限のストリームを生成したいことを付け加えておきます。
私はこの質問をできるだけ短くそして要点を述べようとします。
コマンドラインに3文字入力すると、この計算が完了すると思いました。ただし、そうではありません。なぜそうなのか気になります。
return . take 3 =<< sequence (repeat getChar)
編集:
選択を世代から分離しようとしているので、選択した文字の無限のストリームを生成したいことを付け加えておきます。
I / Oは決定論的にアクションをシーケンスします。つまり、実際に言っているのは、getChar
アクションを無限に実行し、その後、生成されたリストから最初の3つの項目を取得するということです。あなたはおそらくこれがどのように長い時間がかかるかを見ることができます。
I / Oを遅延して(そして非決定的に)実行したい場合はunsafeInterleaveIO
、System.IO.Unsafe
パッケージの関数を使用して実行できます。
必要なものを実装する1つの方法は、たとえば、次のようになります。
import System.IO.Unsafe
lazyIOSequence :: [IO a] -> IO [a]
lazyIOSequence [] = return []
lazyIOSequence (x:xs) = do
first <- unsafeInterleaveIO $ x
rest <- unsafeInterleaveIO $ lazyIOSequence xs
return $ first:rest
test = return . take 3 =<< lazyIOSequence (repeat getChar)
ただし、リストの要素を評価するまで、ユーザーからのデータの実際のプロンプトが遅れるため、希望どおりに動作しない可能性があります。実際、レイジーI / Oストリームは一般に問題があると見なされます(たとえば、「HaskellレイジーI /Oとファイルを閉じる」および「レイジーI/Oの何が悪いのか」という質問を参照してください)。
生産を選択および処理から分離するために現在推奨されている方法は、列挙子、パイプ、またはコンジットなどのさまざまな厳密なストリーム処理ライブラリの1つを使用することです。
=<<
開始の左側の効果の前に、完了の右側の効果=<<
。
=<<
アクションが永久に実行される場合、アクション全体が永久に実行されることを意味します。sequence
アクションのリストを取得し、それらすべてのアクションを実行するアクションを返します。
sequence
アクションの無限のリストを指定すると、それは永久に実行されます。repeat
値を取り、その値の無限のリストを繰り返し与えます。なぜあなたはあなたが経験する行動をとるのか分かりますか?
無限のリストがないため、これは機能します。
sequence (take 3 $ repeat getChar)
しかし、おそらくあなたの実際の選択機能は「最初の3つを取る」よりも複雑です。それは何ですか?