コマンドライン引数
コマンドライン引数をとれるようにする。
import System.Environment import Data.List main = do args <- getArgs progName <- getProgName putStrLn "The arguments are:" mapM putStrLn args putStrLn "The program name is:" putStrLn progName
ビルドして実行
> stack ghc -- --make arg-test.hs [1 of 1] Compiling Main ( arg-test.hs, arg-test.o ) Linking arg-test ... > ./arg-test first second w00t "multi word arg" The arguments are: first second w00t multi word arg The program name is: arg-test
Todoリスト改良する
- タスク閲覧
- タスク追加
- タスク削除
できるようにする
コマンドと引数のリストを受け取って望みの動作を行うI/Oアクションを返す関数を作る。
import System.Environment import System.Directory import System.IO import Data.List import Control.Exception dispatch :: String -> [String] -> IO () dispatch "add" = add dispatch "view" = view dispatch "remove" = remove main = do (command:argList) <- getArgs dispatch command argList add :: [String] -> IO () add [fileName, todoItem] = appendFile fileName (todoItem ++ "\n") view :: [String] -> IO () view [fileName] = do contents <- readFile fileName let todoTasks = lines contents numberedTasks = zipWith (\n line -> show n ++ " - " ++ line) [0..] todoTasks putStr $ unlines numberedTasks remove :: [String] -> IO () remove [fileName, numberString] = do contents <- readFile fileName let todoTasks = lines contents numberedTasks = zipWith (\n line -> show n ++ " - " ++ line) [0..] todoTasks let number = read numberString newTodoItems = unlines $ delete (todoTasks !! number) todoTasks bracketOnError (openTempFile "." "temp") (\(tempName, tempHandle) -> do hClose tempHandle removeFile tempName) (\(tempName, tempHandle) -> do hPutStr tempHandle newTodoItems hClose tempHandle removeFile "todo.txt" renameFile tempName "todo.txt")
ビルド・実行
> stack ghc -- --make todo.hs [1 of 1] Compiling Main ( todo.hs, todo.o ) Linking todo ... > ./todo view todo.txt 0 - 11111111111111 1 - chinchin > ./todo add todo.txt "chinchinchin" > ./todo view todo.txt 0 - 11111111111111 1 - chinchin 2 - chinchinchin > ./todo remove todo.txt 0 > ./todo view todo.txt 0 - chinchin 1 - chinchinchin
dispatch
関数を使うことで簡単に機能を追加することができる。
ファイルとタスクの番号を受け取り、リストの先頭に持ってくるbump
関数を実装してみる
bump :: [String] -> IO () bump [fileName, numberString] = do contents <- readFile fileName let number = read numberString todoTasks = lines contents selectedTask = todoTasks !! number newTodoItems = unlines $ [selectedTask] ++ (delete selectedTask todoTasks) bracketOnError (openTempFile "." "temp") (\(tempName, tempHandle) -> do hClose tempHandle removeFile tempName) (\(tempName, tempHandle) -> do hPutStr tempHandle newTodoItems hClose tempHandle removeFile "todo.txt" renameFile tempName "todo.txt")
ビルド・実行
> stack ghc -- --make todo.hs [1 of 1] Compiling Main ( todo.hs, todo.o ) Linking todo ... > ./todo view todo.txt 0 - chinchin 1 - chinchinchin > ./todo add todo.txt "bump of chicken" > ./todo view todo.txt 0 - chinchin 1 - chinchinchin 2 - bump of chicken > ./todo bump todo.txt 2 > ./todo view todo.txt 0 - bump of chicken 1 - chinchin 2 - chinchinchin
で、できた...
不正な入力に対応する
全て拾うパターンをdispatchの最後に追加する
dispatch :: String -> [String] -> IO () dispatch "add" = add dispatch "view" = view dispatch "remove" = remove dispatch "bump" = bump dispatch command = doesntExist command doesntExist :: String -> [String] -> IO () doesntExist command = putStrLn $ "The " ++ command ++ " command doesn't exist"
また、add, view, remove それぞれの関数でも全てを拾うパターンを追加する。 引数の数が違う場合にユーザーに知らせるようにする。
add :: [String] -> IO () add [fileName, todoItem] = appendFile fileName (todoItem ++ "\n") add _ = putStrLn "The add command takes exactly two arguments" ...
実行・ビルド
> stack ghc -- --make todo.hs [1 of 1] Compiling Main ( todo.hs, todo.o ) Linking todo ... > ./todo oppai The oppai command doesn't exist > ./todo add todo.txt The add command takes exactly two arguments > ./todo view todo.txt hogeee The view command takes exactly one arguments > ./todo remove todo.txt The remove command takes exactly two arguments > ./todo bump The bump command takes exactly two arguments
おk
まだ、./todo で実行するとエラーとなる
> ./todo todo: user error (Pattern match failure in do expression at todo.hs:15:5-21)
またあとで対応する予定
所感
だいぶ実用的なプログラムっぽいし、bump はなんとか自分で書けたので良かった。 (うまく書けているかはわからん)
CLIのプログラムはこのパターンを応用すれば書いていけそう。
dispatch から各コマンドを呼び出すのに関数を返しているが、 このように書いていく感覚を早く掴んで自分でスラスラ書いていけるようになりたい。

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