文字通り、初めて Haskell のコードを書いてみました。
まずは、
ふつうのHaskellプログラミング
を半分ほど読んで雰囲気を掴みました。
そのあとは「最初のお題 CRUD」を書き上げるのに必要なことを Google で検索しまくりました。
Haskell のコードを書くにあたって、次のようなことを考えました。
- Haskell(関数型)らしくなくても良いから、とにかく動くものを書き上げる。
- ループ構造は再帰で実現する。
- 保存したいデータは、関数に渡して持ち回る。
- ハッシュ(連想配列)はタプル(tuple)のリストで代用する。
- 正規表現はパーサコンビネータ(Parsec)で代用する。
- 例外処理(error, catch)は使わない。 使うのであれば、良く理解してからにすべし。
実行サンプル
Haskell 版の動作は次のようになります。
select コマンドは、スペックダウンして引数がなくなっています。
また、メッセージが英文になっています。
$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 6.8.2
$ chmod +x crud.hs
$ ./crud.hs
CRUD> hello
Syntax error.
CRUD> insert "United Kingdom" => "London"
CRUD> insert "Japan" => "Osaka"
CRUD> select
United Kingdom => London
Japan => Osaka
CRUD> insert "Japan" => "Tokyo"
"Japan" already exists.
CRUD> update "Japan" => "Tokyo"
CRUD> select
United Kingdom => London
Japan => Tokyo
CRUD> delete "japan"
"japan" is not found.
CRUD> delete "Japan"
CRUD> select
United Kingdom => London
CRUD> exit
$
|
ソースコード
crud.hs
#!/usr/bin/runghc
import Char
import List
import Text.ParserCombinators.Parsec
strip :: String -> String
strip = reverse . dropWhile isSpace . reverse . dropWhile isSpace
data Cmd = Cmd { aCmd::String, aArg0::String, aArg1::String }
crudInsert tuple hash = -- insert
case (lookup (fst tuple) hash) of
Just val -> Left (show (fst tuple) ++ " already exists.")
Nothing -> Right $ hash ++ [tuple]
crudSelect hash = -- select
putStr $ unlines $ map format hash
where
format t = (fst t) ++ " => " ++ (snd t)
crudUpdate tuple hash = -- update
case (lookup (fst tuple) hash) of
Just val -> Right $ map update hash
Nothing -> Left (show (fst tuple) ++ " is not found.")
where
update t = if (fst t) == (fst tuple) then tuple else t
crudDelete key hash = -- delete
case (lookup key hash) of
Just val -> Right $ delete (key, val) hash
Nothing -> Left (show key ++ " is not found.")
parseCmd :: Parser Cmd
parseCmd =
try(do eof;
return (Cmd "" "" "")) -- CRUD>
<|> try(do cmd <- many1 letter;
eof;
return (Cmd cmd "" "")) -- CRUD> cmd
<|> try(do cmd <- many1 letter;
spaces;
char '"'; arg0 <- many1 (noneOf "\""); char '"';
eof;
return (Cmd cmd arg0 "")) -- CRUD> cmd "arg0"
<|> try(do cmd <- many1 letter;
spaces;
char '"'; arg0 <- many1 (noneOf "\""); char '"';
spaces; string "=>"; spaces;
char '"'; arg1 <- many1 (noneOf "\""); char '"';
eof;
return (Cmd cmd arg0 arg1)) -- CRUD> cmd "arg0" => "arg1"
<|> fail ""
loop :: [(String, String)] -> IO ()
loop hash = do
cs <- putStr "CRUD> " >> getLine
case (parse parseCmd "" $ strip cs) of
Right r -> do
case (aCmd r) of
"insert" | (aArg0 r)/="" && (aArg1 r)/=""
-> case (crudInsert (aArg0 r, aArg1 r) hash) of
Right r -> loop r
Left e -> putStrLn e >> loop hash
"select" | (aArg0 r)=="" && (aArg1 r)==""
-> crudSelect hash >> loop hash
"update" | (aArg0 r)/="" && (aArg1 r)/=""
-> case (crudUpdate (aArg0 r, aArg1 r) hash) of
Right r -> loop r
Left e -> putStrLn e >> loop hash
"delete" | (aArg0 r)/="" && (aArg1 r)==""
-> case (crudDelete (aArg0 r) hash) of
Right r -> loop r
Left e -> putStrLn e >> loop hash
"exit" | (aArg0 r)=="" && (aArg1 r)==""
-> return ()
"" -> loop hash
otherwise -> putStrLn "Syntax error." >> loop hash
Left e -> putStrLn "Syntax error." >> loop hash
main = loop []
|