「ほっ」と。キャンペーン

<   2012年 12月 ( 21 )   > この月の画像一覧

HDBC によるデータベースとのデータのやり取り

Haskell のデータとデータベースのデータの形式が違うので、両者とのやり取りをするには、データの変換が必要だ。HDBC ではそれを toSql と fromSql という二つの関数で行う。それを確かめてみよう。

まず、次のようなテスト用のデータベースを使る。

$ sqlite3 test1.db "create table t1 (id integer, english text, japanese text)"
$ sqlite3 test1.db "insert into t1 (id, english, japanese) values (1, 'John', '金太郎')"
$ sqlite3 test1.db "insert into t1 (id,english,japanese) values (2,'Mary','花子')"
$ sqlite3 test1.db "select * from t1"
1|John|金太郎
2|Mary|花子

ここで、ghci を起動してテストしてみる。

Prelude> :m Database.HDBC Database.HDBC.Sqlite3
Prelude Database.HDBC.Sqlite3 Database.HDBC> toSql "hello"
SqlString "hello"

toSql 関数によって文字列 "hello" が変換されて SqlValue 型のデータ SqlString "hello" になる。これを元の String 型に戻すには fromSql 関数を使う。fromSql 関数では戻り値の型指定が必要だ。

Prelude Database.HDBC.Sqlite3 Database.HDBC> fromSql (toSql "hello") :: String
"hello"

ここで先ほど作成したデータベースに接続してデータを検索してみよう。

Prelude Database.HDBC.Sqlite3 Database.HDBC> conn <- connectSqlite3 "test1.db"

Prelude Database.HDBC.Sqlite3 Database.HDBC> quickQuery conn "select * from t1" []
[[SqlByteString "1",SqlByteString "John",SqlByteString "\233\135\145\229\164\170\233\131\142"],[SqlByteString "2",SqlByteString "Mary",SqlByteString "\232\138\177\229\173\144"]]

quickQuery 関数の戻り値は IO [[ SqlByteString ]] 型になっているのでこのままではターミナルに印字できない。これを IO String 型にする方法を考えないといけない。

Prelude Database.HDBC.Sqlite3 Database.HDBC> map (map (fromSql :: SqlValue -> String)) [[ toSql "hello" ]]
[["hello"]]

それでは、さっきのクエリの結果を変換してみよう。

Prelude Database.HDBC.Sqlite3 Database.HDBC> quickQuery conn "select * from t1" [] >>= \xs -> return (map (map (fromSql :: SqlValue -> String)) xs)
[["1","John","\37329\22826\37070"],["2","Mary","\33457\23376"]]

日本語が数字表記になってしまっているが、putStrLn 関数に渡せばきちんと表示できる。

Prelude Database.HDBC.Sqlite3 Database.HDBC> quickQuery conn "select * from t1" [] >>= \xs -> return (map (map (fromSql :: SqlValue -> String)) xs) >>= \xs -> return (map unwords xs) >>= \xs -> mapM_ putStrLn xs
1 John 金太郎
2 Mary 花子

これらの実験をまとめたテストプログラム test1.hs は次のようになる。

-- filename : test1.hs
import Database.HDBC
import Database.HDBC.Sqlite3
import Data.List

main = do
  conn <- connectSqlite3 "test1.db"
  result <- quickQuery conn "select * from t1" []
  mapM_ putStrLn (map convRow result)

convRow xs = unwords $ map (fromSql :: SqlValue -> String) xs

実行結果は次のようになる。

Prelude> :l test1.hs
[1 of 1] Compiling Main ( test1.hs, interpreted )
Ok, modules loaded: Main.
*Main> main
Loading package transformers-0.3.0.0 ... linking ... done.
Loading package array-0.4.0.0 ... linking ... done.
Loading package bytestring-0.9.2.1 ... linking ... done.
Loading package deepseq-1.3.0.0 ... linking ... done.
Loading package text-0.11.2.3 ... linking ... done.
Loading package mtl-2.1.2 ... linking ... done.
Loading package old-locale-1.0.0.4 ... linking ... done.
Loading package time-1.4 ... linking ... done.
Loading package old-time-1.1.0.0 ... linking ... done.
Loading package containers-0.4.2.1 ... linking ... done.
Loading package convertible-1.0.11.1 ... linking ... done.
Loading package utf8-string-0.3.7 ... linking ... done.
Loading package HDBC-2.3.1.2 ... linking ... done.
Loading package HDBC-sqlite3-2.3.3.0 ... linking ... done.
1 John 金太郎
2 Mary 花子

無事 Haskell からデータベースを操作できたので、目的の連想記憶データベースに一歩近づけた。
[PR]
by tnomura9 | 2012-12-31 18:54 | Haskell | Comments(0)

入れ子の map

map が入れ子のループに使えるのを見つけたのでメモ

Prelude> map (map show) [[1,2],[3,4]]
[["1","2"],["3","4"]]

意味は違うが、次の例も2重ループの処理

Prelude> product $ map sum [[1,2], [3,4]]
21
[PR]
by tnomura9 | 2012-12-31 12:03 | Haskell | Comments(0)

sqlite3 で連想記憶データベース

sqlite データベースを作るといってもその構造は非常にシンプルだ、連想元のキーワードと連想先のキーワードと連想に関するコメントのフィールドを持つレコードを定義するだけだ。具体的には次のようにする。

$ sqlite3 assoc.db "create table aminoacid (src text, dest text, comment text)"

テーブル名が aminoacid になっているのはアミノ酸のデータベースを作ろうと思っているからだ。

データの入力は insert 文で行うのは大変なので次のようなファイルを aadb.csv として作成する。これをデータベースにインポートする目的だ。

単純な構造,Gly,""
単純な構造,Ala,""
塩基性アミノ酸,Lys,""
塩基性アミノ酸,Arg,""
塩基性アミノ酸,His,""
酸性アミノ酸,Asp,""
酸性アミノ酸,Glu,""
アミドをもつ,Asn,""
アミドをもつ,Gln,""
-OHを含む,Ser,""
-OHを含む,Thr,""
-OHを含む,Tyr,""
S原子を含む,Cys,""
S原子を含む,Met,""
環構造をもつ,Phe,""
環構造をもつ,Tyr,""
環構造をもつ,His,""
環構造をもつ,Trp,""
脂肪族,Val,""
脂肪族,Leu,""
脂肪族,Ile,""
その他,Pro,""
Gly,グリシン,最も単純なアミノ酸
Ala,アラニン,グリシンの次に単純なアミノ酸
Val,バリン,側鎖の形がV字型
Leu,ロイシン,バリンでさらに側鎖に-CH2-が増えたもの
Ile,イソロイシン,ロイシンの構造異性体
Gly,Ala,-CH3基が一つ増える
Ala,Val,-CH3基が一つ増える
Val,Leu,-CH2-が一つ増える
Leu,Ile,光学異性体

これを次のようにして aminoacid テーブルにインポートする。

sqlite3 -separator , assoc.db ".import aadb.csv aminoacid"

Mac の sqlite3 では日本語入力ができなかったので、前回紹介した test_yubin を編集して test_assoc を作成する。

#!/bin/sh

read sql
while [ "$sql" != ".quit" ]
do
sqlite3 assoc.db "$sql"
read sql
done

最後に sh test_assoc で連想記憶データベースをテストできる。

$ sh test_assoc

まず連想元にどんなキーワードがあるかを見てみる。重複するものもあるので、distinct 属性をつける。

select distinct src from aminoacid
単純な構造
塩基性アミノ酸
酸性アミノ酸
アミドをもつ
-OHを含む
S原子を含む
環構造をもつ
脂肪族
その他
Gly
Ala
Val
Leu
Ile
グリシン

これらのキーワードの中で、「単純な構造」というキーワードで検索してみる。
select * from aminoacid where src='単純な構造'
単純な構造|Gly|
単純な構造|Ala|

「単純な構造」で連想された「Gly」についてさらに調べてみる。

select * from aminoacid where src='Gly'
Gly|グリシン|最も単純なアミノ酸
Gly|Ala|-CH3基が一つ増える

Gly(グリシン)が最も単純なアミノ酸で、それに-CH3基が一つ増えたのが Ala(アラニン)だということが分かる。Ala についてさらに調べてみよう。

select * from aminoacid where src='Ala'
Ala|アラニン|グリシンの次に単純なアミノ酸
Ala|Val|-CH3基が一つ増える

今度は、Ala を連想先にしている連想にはどのようなものがあるか検索してみよう。

select * from aminoacid where dest='Ala'
単純な構造|Ala|
Gly|Ala|-CH3基が一つ増える

Ala(アラニン)は単純な構造のアミノ酸で、Gly(グリシン)に -CH3 基が増えたものであることが分かる。

連想記憶のデータベースは、単純な構造のデータベースだが、sqlite3 の検索機能と組み合わせることで知識をいろいろな角度から検討してみることができる。
[PR]
by tnomura9 | 2012-12-30 21:22 | Haskell | Comments(0)

Mac で日本語の SQL を実行する

Mac では sqlite3 のインタラクティブモードでは日本語が入力できなかった。幸い、ターミナルでは日本語が使えるので、シェルスクリプトで sqlite3 のフロントエンドを作ってみた。次のスクリプトを test_yubin というファイル名で作成し、yubin.db と同じディレクトリに置いておく。

#!/bin/sh

read sql
while [ "$sql" != ".quit" ]
do
sqlite3 yubin.db "$sql"
read sql
done

そうしてコマンドラインから sh test_yubin で実行すると、プロンプトは出ないものの、SQLで日本語のデータを検索できる。

$ sh test_yubin
select * from master where cho like 'ヤマカワ%'
08207|307 |3070033|イバラキケン|ユウキシ|ヤマカワシンジュク|茨城県|結城市|山川新宿|0|0|0|0|0|0
16202|93303|9330303|トヤマケン|タカオカシ|ヤマカワ|富山県|高岡市|山川|0|0|0|0|0|0
以下省略

.quit と入力すると終了する。

.quit
$

小さい用途ならシェルスクリプトも結使い勝手がいい。

参考サイト

シェルスクリプト入門
[PR]
by tnomura9 | 2012-12-30 19:38 | Haskell | Comments(0)

踊ってみたサイト

愛川こずえ
みうめ
いとくとら
ひま&むにい
のらくら
足太ぺんた
AMU
みこ
マリス
りりあ
わた
りりり

Treetopfan
Nico Nico D@nce (BoutoKM)
Nico Nico Douga reprints (ffstip)

Kalafina Official Youtube Channel
[PR]
by tnomura9 | 2012-12-30 15:32 | ボーカロイド | Comments(0)

sqlite3 によるアミノ酸データベース

sqlite3 の操作の学習用になにか手軽なデータベースはないかと考えていたら、自分の過去記事の「sqlite3 の使い方」にアミノ酸のデータベースを作る記事があった。その記事の CSV のデータをコピーして vi に張りつけ、aminoacid.csv というファイルを作った。

次に、sqlite3 で次のようにして anminoacid.db を作りテーブル aa を作成した。

$ sqlite3 aminoacid.db "create table aa (name text,let3 text,let1 text,polarity text,acidity text,hydropathy real);"

最後に、aminoacid.csv を aminoacid.db の aa テーブルにインポートした。

$ sqlite3 -separator , aminoacid.db ".import aminoacid.csv aa"

そこで sqlite3 のインタラクティブモードに入り、データベースのテストをしてみた。

$ sqlite3 aminoacid.db

データベースのスキーマを確かめた。
sqlite> .schema
CREATE TABLE aa (name text,let3 text,let1 text,polarity text,acidity text,hydropathy real);

sqlite_master テーブルの内容を確かめた。
sqlite> select * from sqlite_master;
table|aa|aa|2|CREATE TABLE aa (name text,let3 text,let1 text,polarity text,acidity text,hydropathy real)

aa テーブルの全データを表示した。
sqlite> select * from aa;

親水性のアミノ酸だけを表示した。
sqlite> select * from aa where polarity="polar";

疎水性親水性指標が正のものを取り出した。
sqlite> select * from aa where hydropathy > 0;

名前の頭文字が 'T' で始まるものを表示した。
sqlite> select * from aa where name like 'T%';

like 検索では % は任意の0個以上の文字列を表し、_ は任意の1文字を表す。次の例は名前が8文字のものを抜き出している。

sqlite> select name from aa where name like '________';

アミノ酸の名前と略号のみを表示した。
sqlite> select name,let3,let1 from aa;

表示をカラム名つきのものにして、カラム幅も調整して表示した。
sqlite> .explain
sqlite> .width 14 4 4 8 16 10
sqlite> select * from aa;

let1 カラムについてソートして表示した。
sqlite> select * from aa order by let1;

acidity が neutral のものを抜き出して、hydropathty 順でソートして表示した。
sqlite> select * from aa where acidity='neutral' order by hydropathy;

polarity(極性)が neutral でないものを抽出した。
sqlite> select * from aa where not acidity='neutral';

polarity が polar で acidity が neutral のものを抽出した。
sqlite> select * from aa where polarity='polar' and acidity='neutral';

hydropathy のデータについて昇順にソートした。カラム名のあとに asc をつけると昇順に、desc をつけると降順にソートされる。
sqlite> select * from aa order by hydropathy asc;

hydropathy のデータについて降順にソートした。
sqlite> select * from aa order by hydropathy desc;

テーブルの行数を数えた。
sqlite> select count(*) from aa;

acidity が 'neutral' のものの行数(レコード数)を数えた。
sqlite> select count(*) from aa where acidity='neutral';

一旦データベースを作ってみると、いろいろな検索や分類が簡単にできるのが分かる。CSVからデータベースへのインポートが簡単にできるので、入力は表計算ソフトで行ってそれを CSV ファイルに出力してデータベースを作ることができる。

sqlite3 のデータベースファイルは一個だけなので、削除するのも、持ち運ぶのも簡単だ。なんでこんな便利なものを今まで放置していたのだろう。
[PR]
by tnomura9 | 2012-12-30 13:44 | Haskell | Comments(0)

sqlite3 で日本語を使う

sqlite3 で日本語のデータが扱えるのだろうか。

Mac のターミナルを起動して LANG の値を出力したらつぎのようになった。

Tomokiyo-no-MacBook-Pro:~ tomokiyo$ echo $LANG
ja_JP.UTF-8

そこで、試しにデータベースを作って日本語のデータを入力してみた。

$ sqlite3 names.db "create table members (mkey integer primary key, name text, age integer);"
$ sqlite3 names.db "insert into members (name, age) values ('桃太郎',18);
$ sqlite3 names.db "insert into members (name, age) values ('かぐや',16);
$ sqlite3 names.db "insert into members (name, age) values ('金太郎',12);

$ sqlite3 names.db "select * from members where name='かぐや'"
2|かぐや|16

無事、日本語での検索ができた。

そこで欲を出して郵便番号のデータベースを作ってみることにした。郵便局のホームページから郵便番号の CSV ファイルをダウンロードした。

ダウンロードした KEN_ALL.CSV は CR-LF 改行の S-JIS ファイルなので nkf を使って次のように LF 改行の UTF-8 ファイルにした。

$ nkf -w -Lu KEN_ALL.CSV > yubin.csv

nkf が Mac に入っていないときは MacPorts がインストールされていれば、

$ port install nkf

でインストールできる。

そこで郵便番号のデータベース yubin.db を次のようにして作った。

$ sqlite3 yubin.db "create table master (dantai text, zip_old text, zip text, ken text, si text, cho text, kenmei text, simei text, chomei text, op1 integer, op2 integer, op3 integer, op4 integer, op5 integer, op6 integer);"

CSV ファイルからのデータのインポートは次のようにした。

$ sqlite3 -separator , yubin.db ".import yubin.csv master"

日本語で検索すると実行できた。

$ sqlite3 yubin.db "select * from master where cho='ヤマカワマチ'"
40203|839 |8390817|フクオカケン|クルメシ|ヤマカワマチ|福岡県|久留米市|山川町|0|0|0|0|0|0
42204|854 |8540074|ナガサキケン|イサハヤシ|ヤマカワマチ|長崎県|諫早市|山川町|0|0|0|0|0|0

これで、sqlite3 で日本語が使えることがわかった。

ところが落とし穴があった。次のように sqlite3 のインタラクティブモードに入ると、日本語の入力ができないことがわかった。sqlite3 のコマンドラインが日本語入力に対応してないためだ。

$ sqlite3 yubin.db

前途多難だ。心配になったので、Haskell の getLine が日本語を受け付けるかどうか試してみた。

Prelude> getLine >>= putStrLn
おはよう
おはよう

こちらのほうは、大丈夫なようだ。

Mac でうまくいったので Ubuntu でもやってみた。yubin.db を作るところまではうまくいったのだが、

$ sqlite3 yubin.db "select * from master where cho='ヤマカワマチ'"

とやっても、レコードが検索できない。かくとうしているうちにフィールドのデータが ヤマカワマチ ではなく、 "ヤマカワマチ" のように引用符つきで表示されているのに気づいた。そこで、csv ファイルを次のように加工して yubin2.csv をつくりインポートしたらうまくいった。

$ sed s/\"//g yubin.csv > yubin2.csv
$ sqlite3 -separator , yubin.db ".import yubin2.csv master"

ところが、Ubuntu の sqlite3 ではインタラクティブモードでも日本語が入力できた。機種によって反応が違うので注意しなければならない。
[PR]
by tnomura9 | 2012-12-29 22:14 | Haskell | Comments(0)

HDBC

Haskell を使って sqlite3 にアクセスする方法は Real World Haskell の第21章に書いてある。詳しくはそちらを読んでもらうことにして、まず sqlite3 をインストールする。

MacPorts をインストールした Mac では、

$ sudo port install sqlite3

Ubuntu では、

$ sudo apt-get install sqlite3
$ sudo apt-get install libsqlite3-dev

Haskell Platform は既にインストールされていると仮定して、HDBC-sqlite3 をインストールする。

$ cabal install HDBC-sqlite3

インストールに成功したら、テスト用のデータベースを作る。

$ sqlite3 test.db "create table t1 (t1key integer primary key,data text,num double)"
$ sqlite3 test.db "insert into t1 (data,num) values ('This is sample data',3)"
$ sqlite3 test.db "insert into t1 (data,num) values ('More sample data',6)"
$ sqlite3 test.db "insert into t1 (data,num) values ('And a little more', 9)"

データベースができたらテストしてみる。

$ sqlite3 test.db "select * from t1"
1|This is sample data|3.0|
2|More sample data|6.0|
3|And a little more|9.0|

いよいよ Haskell と sqlite の連携のテストだ。

$ ghci
Prelude> :m Database.HDBC Database.HDBC.Sqlite3
Prelude Database.HDBC.Sqlite3 Database.HDBC> con <- connectSqlite3 "test.db"
Prelude Database.HDBC.Sqlite3 Database.HDBC> quickQuery con "select * from t1" []
[[SqlByteString "1",SqlByteString "This is sample data",SqlByteString "3.0",SqlNull],[SqlByteString "2",SqlByteString "More sample data",SqlByteString "6.0",SqlNull],[SqlByteString "3",SqlByteString "And a little more",SqlByteString "9.0",SqlNull]]

これで一応 Haskell で sqlite3 を利用するところまではこぎつけた。

HDBC の使い方のまとめ

Prelude> :m Database.HDBC Database.HDBC.Sqlite3
Prelude Database.HDBC.Sqlite3 Database.HDBC> :set prompt "ghci> "
ghci> conn <- connectSqlite3 "test3.db"
ghci> run conn "create table membership (id integer primary key, name text, age integer)" []
0
ghci> commit conn
ghci> run conn "insert into membership (name, age) values ('Snow White', 14)" []
1
ghci> commit conn
ghci> quickQuery conn "select * from membership" []
[[SqlByteString "1",SqlByteString "Snow White",SqlByteString "14"]]
ghci> disconnect conn

データベースに書き込みなどの操作を行うときは run 関数を使うが、commit 関数を実行しないと実際には書き込まれない。データの検索は quickQuery 関数を使う。

HDBC の概略を知りたい時は、

Prelude> :browse Database.HDBC
Prelude> :browse Database.HDBC.Sqlite3
Prelude> :info connectSqlite3

などを活用する。
[PR]
by tnomura9 | 2012-12-29 07:50 | Haskell | Comments(0)

Text.Printf その2

Text.Printf のソースを覗いたら、意外に複雑で Haskell らしくなかった。不思議だなと考えてみたが、どうやら Haskell は可変長引き数が苦手なのではないかと気づいた。

可変長引き数を関数に渡す時、Haskell の場合、リストで渡すのが普通だ。しかし printf の場合、

printf "%s %5d" "Lucky" 777

の様に引き数の型が異なるためにリストとして渡すことができない。このような場合一般的には data 宣言で型の違うデータをラップして、配列の要素として使えるようにする。

Prelude> data Prf = Pstr String | Pint Int
Prelude> let showPrf arg = case arg of (Pstr str) -> str; (Pint i) -> show i
Prelude> let putPrf args = putStrLn $ unwords $ map showPrf args
Prelude> putPrf [Pstr "Lucky", Pint 777]
Lucky 777

しかし、Text.Printf ライブラリの printf は C の printf の使用感をそのままシミュレートできるように工夫してあるので複雑なコードになっているのだ。

その工夫を調べるのも興味が湧くが、連想記憶データベースを作るというテーマと、Text.Printf の解析からHaskell のテクニックを学ぶという2つのテーマが混在するとごちゃごちゃになるので、Text.Printf の話題はしばらく休止する。
[PR]
by tnomura9 | 2012-12-28 12:52 | Haskell | Comments(0)

sqlite3 の復習

sqlite3 の使い方についてすっかり忘れてしまっていたので、Sqlite Tutorial で復習した。Sqlite の特徴については自分の過去記事があった。すっかり忘れている。人生は忘却との戦いだ。

自分の過去記事では Sqlite Tutorial の日本語訳があったようだが、記事がなくなっていた。

英語を読むのが面倒だったので操作例だけ抜き出して、Windows にインストールしていた sqlite3 で試してみた。

次がその抜粋だ。当然のように、端末から入力すると操作学習用のデータベース test.db が作成できた。コンピュータの言語を学習するときは、このように実際の操作例を真似して端末から打ち込んでみるのが一番分かりやすい。

$ sqlite3 test.db
SQLite version 3.0.8
Enter ".help" for instructions
sqlite> .quit
$

$ sqlite3 test.db "create table t1 (t1key INTEGER
   PRIMARY KEY,data TEXT,num double,timeEnter DATE);"


$ sqlite3 test.db "insert into t1 (data,num) values ('This is sample data',3);"
$ sqlite3 test.db "insert into t1 (data,num) values ('More sample data',6);"
$ sqlite3 test.db "insert into t1 (data,num) values ('And a little more',9);"

$ sqlite3 test.db "select * from t1 limit 2";
1|This is sample data|3|
2|More sample data|6|


$ sqlite3 test.db "select * from t1 order by t1key limit 1 offset 2";
3|And a little more|9|


$ sqlite3 test.db ".table"
t1

$ sqlite3 test.db "select * from sqlite_master"
table|t1|t1|2|CREATE TABLE t1 (t1key INTEGER
    PRIMARY KEY,data TEXT,num double,timeEnter DATE)


$ sqlite3 test.db ".dump"
BEGIN TRANSACTION;
CREATE TABLE t1 (t1key INTEGER
    PRIMARY KEY,data TEXT,num double,timeEnter DATE);
INSERT INTO "t1" VALUES(1, 'This is sample data', 3, NULL);
INSERT INTO "t1" VALUES(2, 'More sample data', 6, NULL);
INSERT INTO "t1" VALUES(3, 'And a little more', 9, NULL);
COMMIT;


面倒なようだが、test.db を作っては削除し、作っては削除し、を繰り返すうちに sqlite3 の使い方や考え方が自然にわかってくる。しばらくは、この操作を何回も繰り返し、暗記できるまで続けることにする。

プログラミング上達のコツは頭で考えるのではなく、手で考えられるようになるまで繰り返すことだ。どちらかと言うとスポーツの訓練に似ている。
[PR]
by tnomura9 | 2012-12-28 07:12 | Haskell | Comments(0)