ALGOBIT

2010/11/21

簡易KVS mutable版

Filed under: 離散的な気まぐれ — タグ: — Kohyama @ 02:55

Haskell です.

前回の簡易KVSに, ファイルからのロード, セーブを追加. 操作を破壊的にしたもの.

kvs.hs : ソース

import System.IO
import Data.List
import Data.IORef

-- print an IORef object
p r = readIORef r

-- display contents of a file
cat fn = do
	h <- openFile fn ReadMode
	hGetContents h >>= putStr
	hClose h

-- to bind variable, use like 'r <- load filename'
-- r will be the type of IORef [(Int,String)]
load myRead fn = do
	h <- openFile fn ReadMode
	c <- hGetContents h
	newIORef $ myRead c

save fn r = do
	h <- openFile fn WriteMode
	c <- readIORef r
	hPrint h c
	hClose h

new = newIORef []

ins e r = modifyIORef r (e:)
lup k r = do
	t <- readIORef r
	return (lookup k t)
upd (k,v) r = modifyIORef r
	((\(l, r) -> l ++ (k, v):tail r) . break ((k ==) . fst))
del k r = modifyIORef r ((\(l, r) -> l ++ (tail r)) . break ((k ==) . fst))

「IORef a 型の値」(a は任意の型) を第一引数, 「a 型の値を引数にとって a 型の値を返す関数」を第二引数に modifyIORef を呼び出すと, IORef で参照される値が変更されます.
ので, 前回との違いは,

del k t = (\(l, r) -> l ++ (tail r)) $ break ((k ==) . fst)

のように引数 t を変更した新しいテーブルを返すように書いていた関数を以下のように書き換えます.
すなわち, 左辺で, t を newIORef を使って, IORef 型にした変数 r を使います.
右辺では第一引数を r として, 第二引数を元の関数の右辺として, modifyIORef を呼び出します.

del k r = modifyIORef r ((\(l, r) -> l ++ (tail r)) . break ((k ==) . fst))

test.kvs : テスト用ファイル

[(1, "foo"),
 (2, "bar"),
 (3, "baz")]

使ってみます.

% ghci
GHCi, version 6.10.4: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer ... linking ... done.
Loading package base ... linking ... done.
Prelude> :l kvs.hs              -- 上記ソースをロードします.
[1 of 1] Compiling Main             ( kvs.hs, interpreted )
Ok, modules loaded: Main.
*Main> cat "test.kvs"           -- ファイルの中身を確認
[(1, "foo"),
 (2, "bar"),
 (3, "baz")]
*Main> r <- load (read::String -> [(Int,String)]) "test.kvs"
                                -- test.kvs の中身を [(Int,String)] として解釈し, r に束縛
*Main> p r
[(1,"foo"),(2,"bar"),(3,"baz")]
*Main> ins (4,"qux") r          -- (4, "qux") を挿入
*Main> p r
[(4,"qux"),(1,"foo"),(2,"bar"),(3,"baz")]
*Main> lup 2 r                  -- キーが 2 であるエントリを検索
Just "bar"
*Main> lup 5                    -- キーが 5 であるエントリを検索
                                -- Nothing が返されたときの対処をしていないので
<interactive>:1:0:              -- 実行時例外
    No instance for (Show (IORef [(t, b)] -> IO (Maybe b)))
      arising from a use of `print' at <interactive>:1:0-4
    Possible fix:
      add an instance declaration for
      (Show (IORef [(t, b)] -> IO (Maybe b)))
    In a stmt of a 'do' expression: print it
*Main> upd (2,"quux") r         -- キーが 2 であるエントリの値を "quux" に変更
*Main> p r
[(4,"qux"),(1,"foo"),(2,"quux"),(3,"baz")]
*Main> del 1 r                  -- キーが 1 であるエントリを削除
*Main> p r
[(4,"qux"),(2,"quux"),(3,"baz")]
*Main> save "test2.kvs" r       -- "test2.kvs" に保存
*Main> cat "test2.kvs"          -- "test2.kvs" の中身を確認
[(4,"qux"),(2,"quux"),(3,"baz")]

追記: 2010.11.26
これ KVS って言わないよな… タイトルから来た方すいません.

Copyright © 2010 Yoshinori Kohyama All Rights Reserved.