すごいH本読書会 #7
そのまえに...
- I/Oアクションとは何か?
- I/Oアクションはどう入出力を可能にするか?
- 実際にI/Oアクションが実行されるタイミングは?
> 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
> :t putStrLn
putStrLn :: String -> IO ()
> :t putStrLn "Hello, world"
putStrLn "Hello, world" :: IO ()
部分適用したやつも:t
できるのはじめて知った
入出力を読んだり,ファイルを読み書きしたりなどの 副作用 を含む動作をして結果を返す何か
main = do
putStrLn "Hello, what's your name?"
name <- getLine
putStrLn $ "Hey " ++ name ++ ", you rock!"
_人人人人人人_
> 突然のdo <
 ̄Y^Y^Y^Y^Y ̄
do
によって3つのアクションをひとつにしている(:t main
してみる)- なぜなら
main
で実行されるのは ひとつ のI/Oアクション
main = do
putStrLn "Hello, what's your name?"
name <- getLine
putStrLn $ "Hey " ++ name ++ ", you rock!"
_人人人人人人人人人人人人_
> 突然のname <- getLine <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y ̄
> :t getLine
getLine :: IO String
IO
の後ろのやつを取り出すイメージdo
の最後以外に書ける=
じゃダメ
line = getLine
line <- getLine
main
という名前をつけてプログラムを起動したときdo
ブロックの中にいるときghci
> :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
putStr
putChar
print
制御構文っぽいけど普通の関数.Bool
とI/Oアクションを受け取って,True
の場合は実行,False
の場合はreturn ()
する
import Control.Monad
main = do
input <- getLine
when (input == "SWORDFISH") $ do
putStrLn input
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
[(), (), ()]
さっきのsequence
のパターンは頻発するので,map
できるようにしたやつ.mapM_
は結果を棄てる.
> mapM print [1, 2, 3]
1
2
3
[(), (), ()]
> mapM_ print [1, 2, 3]
1
2
3
I/Oアクションを受け取り,それを永遠に繰り返すI/Oアクションを返す.
import Control.Monad
import Data.Char
main = forever $ do
putStr "Give me some input: "
l <- getLine
putStrLn $ map toUpper l
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
にしたいときに使うとよい.
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
を閉じないといけない
> :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
使え.
いわゆる例外処理
> :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
コマンドを作ってください.
> sudden do
_人人人人人人_
> 突然のdo <
 ̄Y^Y^Y^Y^Y ̄