Skip to content

Instantly share code, notes, and snippets.

@yashigani yashigani/main.md
Last active Dec 15, 2015

Embed
What would you like to do?
すごいH本読書会 in 大阪 #7

すごいH本読書会 #7

8章 入出力

Hello, world

そのまえに...

入出力(I/O)の基本

  • I/Oアクションとは何か?
  • I/Oアクションはどう入出力を可能にするか?
  • 実際にI/Oアクションが実行されるタイミングは?

Hello, world

> echo 'main = putStrLn "Hello, world"' > helloworld.hs
> ghc --make helloworld
> ./helloworld
Hello, world

余談

スクリプトの実行はrunghc

> runghc helloworld.hs
"Hello, world"
  • zshならalias -sしておくと便利
> alias -s hs=runghc
> ./helloworld.hs
Hello, world

putStrLnってなんぞや

> :t putStrLn
putStrLn :: String -> IO ()
> :t putStrLn "Hello, world"
putStrLn "Hello, world" :: IO ()

部分適用したやつも:tできるのはじめて知った

I/Oアクションとは

入出力を読んだり,ファイルを読み書きしたりなどの 副作用 を含む動作をして結果を返す何か

I/Oアクションをまとめる

main = do
    putStrLn "Hello, what's your name?"
    name <- getLine
    putStrLn $ "Hey " ++ name ++ ", you rock!"

do!?

_人人人人人人_
> 突然のdo <
 ̄Y^Y^Y^Y^Y ̄

3つのI/Oアクションがひとつにまとまっている!

  • doによって3つのアクションをひとつにしている(:t mainしてみる)
  • なぜならmainで実行されるのは ひとつ のI/Oアクション
main = do
    putStrLn "Hello, what's your name?"
    name <- getLine
    putStrLn $ "Hey " ++ name ++ ", you rock!"

name <- getLine!?

_人人人人人人人人人人人人_
> 突然のname <- getLine <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y ̄

束縛

> :t getLine
getLine :: IO String
  • IOの後ろのやつを取り出すイメージ
  • doの最後以外に書ける
  • =じゃダメ
line = getLine
line <- getLine

I/Oアクション実行のタイミング

  • mainという名前をつけてプログラムを起動したとき
  • doブロックの中にいるとき
  • ghci

doとか<-ってなんなの

do

> :t do
<interactive>:1:1: Empty 'do' block
> :t do print
do print :: Show a => a -> IO ()
> :t do print "hoge"
do print "hoge" :: IO ()

doは↓のsyntax suger

do { x ; result <- y ; foo result }

-- x >>
--     y >>= \result -> foo result

:t (>>)したらMonadとか出てきた.わからん!

<-

> :t (<-)
<interactive>:1:2: parse error on input '<-'

ぐぬぅ.Hoogleに聞いたら<-はキーワードらしい

逆順に表示する

returnは値からI/Oアクションを生成する関数.いわゆるreturnとは違うので注意.

main = do
    line <- getLine
    if null line
        then return ()
    else do
        putStrLn $ reverseWords line
        main

reverseWords :: String -> String
reverseWords = unwords . map reverse . words

便利なI/O関数

出力系

  • putStr
  • putChar
  • print

when

制御構文っぽいけど普通の関数.BoolとI/Oアクションを受け取って,Trueの場合は実行,Falseの場合はreturn ()する

import Control.Monad

main = do
    input <- getLine
    when (input == "SWORDFISH") $ do
        putStrLn input

sequence

I/Oアクションのリストを受け取り,順に実行するI/Oアクションを返す.

main = do
    a <- getLine
    b <- getLine
    c <- getLine
    print [a, b, c]
main = do
    rs <- sequence [getLine, getLine, getLine]
    print rs

mapするだけだとI/Oアクションでなく,I/Oアクションのリストが得られる.

> sequence $ map print [1, 2, 3]
1
2
3
[(), (), ()]

mapM, mapM_

さっきのsequenceのパターンは頻発するので,mapできるようにしたやつ.mapM_は結果を棄てる.

> mapM print [1, 2, 3]
1
2
3
[(), (), ()]
> mapM_ print [1, 2, 3]
1
2
3

forever

I/Oアクションを受け取り,それを永遠に繰り返すI/Oアクションを返す.

import Control.Monad
import Data.Char

main = forever $ do
    putStr "Give me some input: "
    l <- getLine
    putStrLn $ map toUpper l

forM

mapMの引数が逆になったやつ.ラムダ式を使うといい感じに書ける.

import Control.Monad

main = do
    colors <- forM [1,2,3,4] $ \a -> do
        putStrLn $ "Which color do you associate with the number " ++ show a ++ "?"
        color <- getLine
        return color
    putStrLn "The colors that you associate with 1, 2, 3 and 4 are: "
    mapM putStrLn colors

doを使ってアクションをmapしてsequenceにしたいときに使うとよい.

9章 もっと入力,もっと出力

ファイルとストリーム

ファイルのリダイレクト

import Control.Monad
import Data.Char

main = forever $ do
    l <- getLine
    putStrLn $ map toUpper l
> ./capslocker.hs < haiku.txt
I’M A LIL’ TEAPOT
WHAT’S WITH THAT AIRPLANE FOOD, HUH?
IT’S SO SMALL, TASTELESS

入力ストリームから文字列を得る

さっきのはforeverで一行ごとに読み込んでたけど,getContentsにやらせてみる.

import Control.Monad
import Data.Char

main = do
    contents <- getContents
    putStrLn $ map toUpper contents
> ./capslocker.hs < haiku.txt
I’M A LIL’ TEAPOT
WHAT’S WITH THAT AIRPLANE FOOD, HUH?
IT’S SO SMALL, TASTELESS
> ./capslocker.hs
hey ho
HEY HO
lets go
LETS GO

どっちもOK!

main = do
    contents <- getContents
    putStr $ shortLinesOnly contents

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

入力を変換する

interactを使ってみる

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
> ./palindrome.hs
hehe
not a palindrome
ABCBA
palindrome
cookie
not a palindrome
> ./palindrome.hs < words.txt
not a palindrome
palindrome
palindrome
palindrome

文字列を別の文字列に変換するプログラムを書いたのに,1行ごとに処理するようにも動く.入力に対して,どういう出力になるかを考えればOK

ファイルの読み書き

> :t openFile
openFile :: FilePath -> IOMode -> IO Handle
import System.IO

main = do
    handle <- openFile "baabaa.txt" ReadMode
    contents <- hGetContents handle
    putStr contents
    hClose handle

openFileは自分でhandleを閉じないといけない

withFile

> :t withFile
withFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r
import System.IO

main = do
    withFile "baabaa.txt" ReadMode $ \handle -> do
        contents <- hGetContents handle
        putStr contents

withFileはやってくれる.withFile使え.

bracket

いわゆる例外処理

> :t bracket
bracket :: IO a -> (a -> IO b) -> (a -> IO c) -> IO c

withFileを実装すると

withFile :: FilePath -> IOMode -> (Handle -> IO a) -> IO a
withFile path mode f = bracket (openFile path mode)
    (\handle -> hClose handle)
    (\handle -> f handle)

正常に動作したときもエラーが起きたときも2引数目の処理が呼ばれる -> finally

ハンドル

それぞれハンドルを対象にしたやつ

  • hGetLine
  • hPutStr
  • hPutStrLn
  • hGetChar

もっと便利なやつ

  • readFile
  • writeFile
  • appendFile
import System.IO

main = do
    contents <- readFile "baabaa.txt"
    putStr contents
import System.IO
import Data.Char

main = do
    contents <- readFile "baabaa.txt"
    writeFile "baabaacaps.txt" $ map toUpper contents

便利!

課題

Twitterやchatをしている際,「突然の○○」でオチをつけたくなることありませんか? 無い人は今すぐ病院に行くことをオススメします. 「突然の○○」を表示する,suddenコマンドを作ってください.

Usage

> sudden do
_人人人人人人_
> 突然のdo <
 ̄Y^Y^Y^Y^Y ̄
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.