fugafuga.write

日々のログ

すごいH本 part60

ファイルとストリーム

ファイルの内容を読み込んで大文字にして返すプログラム

haiku.txt

I'm a cat
nyaaan nyaaan nyaaaan
nyaaan nyaaaan nyaaaaaaaaaan

実装

import Control.Monad
import Data.Char

main = forever $ do
    l <- getLine
    putStrLn $ map toUpper l

ビルド

> stack ghc -- --make capslocker.hs
[1 of 1] Compiling Main             ( capslocker.hs, capslocker.o )
Linking capslocker ...

実行する

> ./capslocker < haiku.txt
I'M A CAT
NYAAAN NYAAAN NYAAAAN
NYAAAN NYAAAAN NYAAAAAAAAAAN
capslocker: <stdin>: hGetLine: end of file

ストリームから文字列を得るプログラム

import Data.Char

main = do
    contents <- getContents
    putStr $ map toUpper contents

実行

> ./capslocker < haiku.txt
I'M A CAT
NYAAAN NYAAAN NYAAAAN
NYAAAN NYAAAAN NYAAAAAAAAAAN

端末から入力を読み込む

> ./capslocker
hoge
HOGE
foo fuga
FOO FUGA

getContentsは遅延I/Oなので必要な時に入力から行を読み込む。

getContents の結果が contents に束縛される時、 文字列としてではなく、最終的に文字列に評価されるプロミス(promise)として メモリ上に置かれる。

contents に toUpper をマップする時もまだプロミスのままで、 putStr が呼ばれるとEOFが入力されるまで入力をとってくる。

入力を受け取り、10文字より短い行だけを出力するプログラムを作る

main = do
    contents <- getContents
    putStr (shortLinesOnly contents)

shortLinesOnly :: String -> String
shortLinesOnly = unlines . filter (\line -> length line < 10) . lines

lines はこんな動き

*Main Lib> lines "aaaaa\nbbbbb\nccccc"
["aaaaa","bbbbb","ccccc"]

filter で10文字未満の長さの要素だけ返す

unlines はこんな動き

*Main Lib> unlines ["aaaaa","bbbbb","ccccc"]
"aaaaa\nbbbbb\nccccc\n"

入力するファイル

i'm short
so am i
i am a looooooooooooooooooooooooong line!!!!
yeah i'm long so what hahahahahaha!!!!
short line
loooooooooooooooooooooooong
short

ビルドして実行する

> stack ghc -- --make shortlinesonly.hs
[1 of 1] Compiling Main             ( shortlinesonly.hs, shortlinesonly.o )
Linking shortlinesonly ...
> ./shortlinesonly < shortlines.txt
i'm short
so am i
short

入力を変換する

入力を文字列として受け取り、関数で変換、結果を出力する。という 操作は頻出するので、interact という便利な関数が用意されている

先程のプログラムを interact を用いて書き換える

main = interact shortLinesOnly

shortLinesOnly :: String -> String
shortLinesOnly = unlines . filter (\line -> length line < 10) . lines

interact は String -> String 型の関数を受け取り、 入力にその関数を適用して、返ってきた結果を出力する。

入力を行毎に読み込んで、回文かどうか出力するプログラムを書く

respondPalindromes :: String -> String
respondPalindromes =
    unlines .
    map (\xs -> if isPal xs then "palindrome" else "not a palindrome") .
    lines

isPal :: String -> Bool
isPal xs = xs == reverse xs

main = interact respondPalindromes

ビルド・実行

> stack ghc -- --make palindrome.hs
[1 of 1] Compiling Main             ( palindrome.hs, palindrome.o )
Linking palindrome ...
> ./palindrome
hehe
not a palindrome
ABCBA
palindrome
11223344332211
palindrome

所感

入力 -> 変換 -> 出力 の流れを体験できるようになった。

遅延I/Oのイメージは関数の遅延評価と同じでなんとなく把握できたが、 遅延I/Oだとなにが嬉しいのかがいまいちわからん。

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!

  • 作者: MiranLipovaca
  • 出版社/メーカー: オーム社
  • 発売日: 2017/07/14
  • メディア: Kindle版
  • 購入: 4人 クリック: 9回
  • この商品を含むブログを見る