2013-09-02 15 views
10

私は、ユーザからの任意の入力行を読み込まなければならないhaskellのプログラムを持っています。ユーザが終了すると、蓄積された入力を関数に送る必要があります。命令型プログラミング言語ではユーザ入力によるHaskellループループ

これは次のようになります。私はHaskellの同等がありますかどうかを知りたいので、私はHaskellで行うことは、これは非常に難しい

content = '' 
while True: 
    line = readLine() 
    if line == 'q': 
     break 
    content += line 
func(content) 

答えて

7

ハスケルではかなり簡単です。最も難しいのは、ユーザー入力のシーケンスを累積したいということです。命令型言語ではこれを行うためにループを使用しますが、Haskellでは標準的な方法は再帰的ヘルパー関数を使用することです。

getUserLines :: IO String      -- optional type signature 
getUserLines = go "" 
    where go contents = do 
    line <- getLine 
    if line == "q" 
     then return contents 
     else go (contents ++ line ++ "\n")  -- add a newline 

これは実際Stringを返すIOアクションの定義である:それは次のようになります。それはIOアクションなので、=代入構文ではなく、<-構文を使用して返された文字列にアクセスします。概要を知りたい場合は、The IO Monad For People Who Simply Don't Careとお読みください。

あなたが

import Control.Applicative ((<$>)) 

input <- unlines . takeWhile (/= "q") . lines <$> getContents 

ような何かを行うことができ

>>> str <- getUserLines 
Hello<Enter>  -- user input 
World<Enter>  -- user input 
q<Enter>   -- user input 
>>> putStrLn str 
Hello   -- program output 
World   -- program output 
+1

今、あなたは本当にハスケルでこれを書いていますか?毎回 'content'に' line'を付けるとパフォーマンスが悪くなります。あなたが最後に望む 'contents'は、' getContents'への一回の呼び出しが与える接頭辞です。 – nickie

+1

それはかなりのポイントですが、IOモナド(これはおそらく初心者のためにハスケルの最も混乱している部分です)で作業するための感触を得るために、これを "最初から"行う方法を説明する価値があると思いました。また、ユーザーの入力を入力から分離するという利点もありますが、入力には処理が含まれていません。 'getContents.'についての付録を追加します。 –

+0

OK、要点、私は最初の-1を取り戻しています。しかし、教育の目的は別に残して、私はこの悪いハスケルのようなコードを検討したいと思います。気になる人のために、少なくとも... :-) – nickie

16

ハスケル反復は、再帰です。また、入力行を読む必要がある場合は、IOモナドで作業する必要があります。一般的な画像です:

import Control.Monad 

main = do 
    line <- getLine 
    unless (line == "q") $ do 
    -- process line 
    main 

あなただけcontent内のすべての読み出し線を蓄積したい場合は、あなたがそれを行う必要はありません。 getContentsを使用すると、すべてのユーザー入力を(遅延して)取得します。 'q'が表示されたら停止してください。かなり慣用Haskellでは、すべての読み取りが単一のコード行で行うことができる:あなたがを右から左へのコードの最初の行を読めば

main = mapM_ process . takeWhile (/= "q") . lines =<< getContents 
    where process line = do -- whatever you like, e.g. 
          putStrLn line 

、それは言う:

  1. GETユーザーが入力として提供するものすべて(恐れず、これは怠惰です)。

  2. それをそのまま行分割します。

  3. "q"と等しくない限り、そのような行が見えるときに停止します。

  4. とし、各行にprocessを呼び出します。

すでに理解していない場合は、Haskellチュートリアルを熟読する必要があります。

+0

私はreadLnの代わりにgetLineを使用して実行可能なバージョンを取得し、 'import Control.Monad'を追加します – jev

+0

@jev、合意しました。もっと一般的にするために 'readLn'を使いましたが、単に行を読みたい人にとっては混乱するかもしれません。 – nickie

1

次に入力は、ユーザがまで書きました(は含まない)何だろう、このようにプロンプ​​トGHCiの時にこの機能を使用することができますq。

2

この週末に出て来ている、pipes-4.0を使用して:

メモリにすべての行を読み込みます
import Pipes 
import qualified Pipes.Prelude as P 

f :: [String] -> IO() 
f = ?? 

main = do 
    contents <- P.toListM (P.stdinLn >-> P.takeWhile (/= "q")) 
    f contents 

。それが生成されているようしかし、あなたは、あまりにも、それぞれの行を処理することができます入力をストリーミングし、メモリに複数の行をロードすることはありません

f :: String -> IO() 

main = runEffect $ 
    for (P.stdinLn >-> P.takeWhile (/= "q")) $ \str -> do 
     lift (f str) 

を。

関連する問題