haskell-ja > Archives > 2012/02/10

2012/02/10 08:14:19 UTC[1..100]>>=pen
#
{-
標準入力から先頭が 'z' で始まる入力行があるまで
input.txt の内容を繰り返し output.txt 書き出す。
-}
main = do
  cs <- getData
  writeFile "output.txt" cs

getData :: IO String
getData = do
  xs <- readFile "input.txt"
  (c:_) <- getLine
  if c == 'z' then return ""
    else do
      ys <- getData
      return $ xs ++ ys
#
上記のプログラムで先頭が z で始まらない行を入力するたび output.txt に出力されることを期待したのですが input.txt のオープンのみ行なわれ z行が入って初めて出力が始まるようです。
#
非z行が入るたび出力が起こるようにするにはどう書くのがよいでしょう。できるだけ簡単なのが希望。output.txt のオープンは一回だけにしたい。
2012/02/10 08:27:49 UTCnobsun
#
getLineが標準入力を読んでいるようにみえますけど。
2012/02/10 08:38:30 UTCnobsun
#
ああそういう意図か
#
ちゅうか、getData呼ぶ度にreadFileが呼ばれてるけど。。。
#
遅延IOに割り込みをかけるってことかしらん
#
非z行が入るたびに、何をどれだけ、output.txtに出力したいのかしらん?
2012/02/10 08:53:41 UTCnobsun
#
これだと、getLineを呼んだときにはxsはすこしも読み込まれてない。最初にzを入れるとなにも出力されない。最初に"a"を入力するとまた、getDataが呼ばれる。次に"z"をいれるとgetDataから""がかえる。xsがinput.txtの内容にそくばくされて、それが csに束縛されてwriteFileが呼ばれて、実際に入力がおこるとかなんとか。。。
2012/02/10 08:58:47 UTCnobsun
#
しかも、"a"をいれた回数と同じ回数分input.txtファイルが最後に出力されるようにみえるなぁ。
2012/02/10 09:13:13 UTCnobsun
#
"z"を入れたときの動作はどういうのを期待してますか?全部中断?
2012/02/10 09:15:42 UTC[1..100]>>=pen
#
中断じゃなくて、それまでに非z行入力した回数分 input.txt の内容が output.txt に出力される、です。
2012/02/10 09:16:14 UTCnobsun
#
ああそういうことですね。
#
非z入力のたびに、input.txtファイルの内容を全部output.txtに書く。zが入れば終了ということでいいですか?
2012/02/10 09:20:16 UTC[1..100]>>=pen
#
そうです。
2012/02/10 09:20:29 UTCnobsun
#
でもinput.txtの内容を全部メモリにおくようなことはしたくない?
2012/02/10 09:21:37 UTC[1..100]>>=pen
#
今回入力ファイルを固定したのはわかりやすくするためだけで本来は変化することを想定してます。
2012/02/10 09:22:44 UTCnobsun
#
別のファイルになるということですか。それとも同じファイルでということですか?
#
ああファイルのオープンは一度だけだから同じファイルか。
2012/02/10 09:26:32 UTC[1..100]>>=pen
#
元々は標準入力(またはでかいファイルからの入力)を受けて "---" のような区切り行が来たら出力ファイルを切り替える。つまり入力を分割してファイルに出力するコマンドを作りたいという問題から始まりました。
2012/02/10 09:32:35 UTCnobsun
#
そっちのほうがずっとやさしいような
2012/02/10 09:40:26 UTC[1..100]>>=pen
#
出力が始まるタイミング知りたくて。
#
できれば入力と出力が producer と consumer になって欲しいなと。
#
で、しかもハンドル使わずに書ければ楽だなと。
2012/02/10 14:14:44 UTCnobsun
#
{-# LANGUAGE TypeOperators
  #-}
module Main where

import Data.OI

main :: IO ()
main = run pmain

pmain :: ((String, [String]), ()) :-> ()
pmain = timing |>| reader |>| writer

timing :: String :-> [String]
timing = getContents' |> (takeWhile (('z'/=).head) . lines)

reader :: [a] -> [String] :-> [String]
reader = zipWithOI (const $ readFile' "input.txt")

writer :: [String] -> () :-> ()
writer = writeFile' "output.txt" . unlines

getContents' :: String :-> String
getContents' = iooi getContents

readFile' :: FilePath -> String :-> String
readFile' = iooi . readFile

writeFile' :: FilePath -> String -> () :-> ()
writeFile' = (iooi .) . writeFile
#
こんなん。readFileはその度によんでます。writeFileは一回だけ。oi-0.0.5 パッケージをつかってください。
#
ああ書き出しのタイミングをチェックしていないなぁ。たぶん創造どおり動くはず。
2012/02/10 14:21:16 UTC[1..100]>>=pen
#
{-
標準入力を区切り行 "---" を目印に分割して別々のファイルに出力する。
区切り行の次行の文字列をその出力ファイル名にする。
-}
import List

main = do
  cs <- getContents
  mapM_ f $ groupBy (\_ b -> b /= "---") $ lines cs
    where
      f ("---":xs@(name:_)) = writeFile name $ unlines xs
#
というのが元々書いていたコードです。区切り行がなかなか出てこないとき入力が溜まってオーバフローが起きないかと心配になって出力がどうなっているか調べようとしたのが先に書いたコードです。
2012/02/10 14:23:28 UTCnobsun
#
さきほどのコードもダメでした。zが押されたタイミングで書き込まれるようです。
2012/02/10 18:28:44 UTC[1..100]>>=pen
#
「心配になって」なんて言ってないで確かめてみようと思って (echo ---; echo output.txt; yes) | runhaskell foo.hs (foo.hs が上記のコードとする)したら次の区切り行は出てこないのに output.txt に出力し始めていた。???混乱している。
#
上の分割コマンドのコードと以前の readFile を使ったコードは分けて考えるべきか。