09

26

[Haskell] すごいHaskell本 9.5 ToDoリストをもっと楽しむ の宿題

2012.09.26(23:34)

すごいHaskell本 9.5 ToDoリストをもっと楽しむ の 197ページ
「いくぶん退屈なので、このプログラムを完全に不正な入力に対処するようにするのは読者の皆さんへの練習問題にしておきます」

宿題やりました。あちこちにエラー処理を入れたら汚いプログラムになってしまいました。(おそらく、まだエラー処理が不十分です) しかし、これはあとで出てくるモナドできれいになるというストーリーだと思うので、このまま答えとして載せておきます。

ソースコードの1行目に以下のようなすべてのワーニングをエラーとみなす指示をいれておくと、あちこちで型修飾が必要などといろいろ教えてくれるので勉強になります。

解いている途中で、if then else をいったん使うと、それ以降の処理は then側か else側かのどちらかにぶらさげなければならないことに気づきました。普通のC言語とかの構造化プログラミングと違います。

{-# OPTIONS -Wall -Werror #-}
-- 9.3 ToDo List
import System.Environment (getArgs)
import System.Directory (removeFile, renameFile, doesFileExist)
import System.IO (openTempFile, hClose, hPutStr)
import Data.List (delete)
import Control.Exception (bracketOnError)
import Control.Monad (when)

test :: [String] -> IO ()
test [] = return ()
test [one] = putStrLn one
test [one,two] = putStrLn $ "one:" ++ one ++ " two:" ++ two
test lists = do
  let len = length lists
  putStrLn $ "length = " ++ show len
  mapM_ putStrLn lists
  let str = take 3 $ map ((\n -> show n)::Int->String) [0..]
  mapM_ putStrLn str
  let args = zipWith 
                ((\n line -> show n ++ " - " ++ line)::Int->String->String) 
                [0..] lists
  -- args         may be ["0 - arg1","1 - arg2","2 - arg3",...]
  -- unlines args may be "0 - arg1\n1 - arg2\n2 - arg3\n...."
  putStr $ unlines args
  return ()

add :: [String] -> IO ()
add [fileName, todoItem] = appendFile fileName (todoItem ++ "\n")
add _ = putStrLn "add [fileName, todoItem]"

view :: [String] -> IO ()
view [fileName] = do
    b <- doesFileExist fileName -- IO Bool
    if b == False
      then putStrLn $ "file not found " ++ fileName
      else do
        contents <- readFile fileName
        let todoTasks = lines contents
            numberedTasks = zipWith 
                               ((\n line -> show n ++ " - " ++ line)::Int->String->String)
                               [0..] todoTasks
        putStr $ unlines numberedTasks
view _ = putStrLn "view [filename]"

remove :: [String] -> IO ()
remove [fileName, numberString] = do
    b <- doesFileExist fileName -- IO Bool
    if b == False
      then putStrLn $ "file not found " ++ fileName
      else do
        contents <- readFile fileName
        let todoTasks = lines contents
            numberedTasks = zipWith 
                               ((\n line -> show n ++ " - " ++ line)::Int->String->String)
                               [0..] todoTasks
        putStrLn "These are your TO-DO items:"
        mapM_ putStrLn numberedTasks
        let number = read numberString
            len = length todoTasks
        if (number < 0 || number >= len)
          then (do putStrLn $ "number error: " ++ show number
                   return ())
          else do
            let 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")
remove _ = putStrLn "remove [fileName, numberString]"

doesntExist :: String -> [String] -> IO ()
doesntExist command _ = do
  putStrLn $ "The " ++ command ++ " command doesn't exist"
  add []
  view []
  remove []

dispatch :: String -> [String] -> IO ()
dispatch "test" = test
dispatch "add" = add
dispatch "view" = view
dispatch "remove" = remove
dispatch command = doesntExist command

main :: IO ()
main = do
  args <- getArgs
  when (length args > 0) $ do
    let (command:argList) = args
    putStrLn command
    dispatch command argList
  return ()
  
プロフィール

島敏博

Shima Toshihiro 島敏博
信州アルプスハイランド在住。HaskellとElixirが好き。組み込みソフトウェアアーキテクト、C++プログラマ、山歩き、美術館巡り、和食食べ歩き、日本赤十字社救急法指導員、インデックス投資、クラシック音楽、SESSAME会員、状態マシン設計、モデル駆動開発、ソフトウェアプロダクトライン、Rubyist、実践ビジネス英語

■ ツイッター
http://twitter.com/saltheads
■ Facebook
http://www.facebook.com/saltheads
■ Qiita
http://qiita.com/saltheads

印刷する場合は、ブラウザの印刷メニューではなく、このページの上から3cmくらいの青いところにある、「印刷」を押してみてください。少しうまく印刷できます。まだ完全ではないのですが、これで勘弁してください。


カテゴリ
最新記事
月別アーカイブ
最新コメント
検索フォーム
リンク
sessame
RSSリンクの表示