Prolog
egison

EgisonでPrologっぽいことをやってみる

More than 1 year has passed since last update.

はじめに

最近,PrologでスキーマレスなNoSQLに目覚めて楽しいです.サザエさん程度なら,学習もし易いですし,言語仕様も細かく覚える必要がありません.優しい言語で,素人でもデータベースを記述することができれば,プログラマがだいぶ楽になるんじゃないかなと思うわけですね.もちろん,規則を書かなければの話ですが.

Prologには連言(AND)が使えます.質問を投げるとき,複数の述語を投げることで,質問の条件を細かく指定することができます.例えば,プリキュア戦士やスーパー戦隊などのスキーマを,以下のような述語で与えたとします.

fun.pro
warrior(nagisa_misumi, futari_ha, black).
warrior(honoka_yukishiro, futari_ha, white).
warrior(hikari_kujo, max_heart, luminus).
warrior(saki_hyuga, splash_star, bloom).
warrior(mai_misho, splash_star, eaglette).
warrior(nozomi_yumehara, yes, dream).
warrior(rin_natsuki, yes, rouge).
warrior(urara_kasugano, yes, lemonade).
warrior(komachi_akimoto, yes, mint).
warrior(karen_minazuki, yes, aqua).

warrior(geki, juranger, tirano).
warrior(goushi, juranger, manmos).
warrior(dan, juranger, torikera).
warrior(boy, juranger, tiger).
warrior(mei, juranger, ptera).
warrior(burai, juranger, dragon).

series(precure, splash_star).
series(precure, futari_ha).
series(precure, yes).
series(super_squadron, juranger).

warriorとseriesは名前で結びついているので,連言を用いれば,特定のシリーズ名に対応したキャラクター名を推論することができます.

test.pro
% XYに当てはまる事実を推論する
1 ?- warrior(X, Y, _), series(precure, Y).
% X = nagisa_misumi,
% Y = futari_ha ;
% X = honoka_yukishiro,
% Y = futari_ha ;
% X = saki_hyuga,
% Y = splash_star ;
% X = mai_misho,
% Y = splash_star ;
% X = nozomi_yumehara,
% Y = yes ;
% X = rin_natsuki,
% Y = yes ;
% X = urara_kasugano,
% Y = yes ;
% X = komachi_akimoto,
% Y = yes ;
% X = karen_minazuki,
% Y = yes ;

% 名前だけの答えをリストにする
2 ?- findall(X, (warrior(X, Y, _), series(precure, Y)), List).
% List = [nagisa_misumi, honoka_yukishiro, saki_hyuga, mai_misho, nozomi_yumehara, rin_natsuki, urara_kasugano, komachi_akimoto, karen_minazuki].

% findall([X, Y], (warrior(X, Y, _), series(precure, Y)), List).
% この形式だと,[名前, シーズン名]で結果が得られます

Prologが強力なところは,わざわざ文字列だとか,数値だとか気にすることがない点です.findallは少しむずかしい構文ではありますが,使い方を覚えれば,リストで結果を得るのは造作もありません.Prologサーバを立てて,データとプログラミングを切り分けることができれば,SQLと比べてとても扱いやすくなるのではないかと思います.

さて,表題の件なのですが,最近,スキー旅行中に某R社の社員からEgisonを勧められました.EgisonはHaskellベースの関数型言語の一つで,パターンマッチに特化した言語だそうです.PrologでできることはEgisonでもできるということなので,早速ですが本当にできるのかどうか検証してみました.

Egisonの実装

先ほどPrologで書いた内容を,Egisonでも表現します.

;;; https://gist.github.com/GRGSIBERIA/0b54694966aa4a104688
(define $warriors
  {["nagisa_misumi" "futari_ha" "black"]
   ["honoka_yukishiro" "futari_ha" "white"]
   ["hikari_kujo" "max_heart" "luminus"]
   ["saki_hyuga" "splash_star" "bloom"]
   ["mai_misho" "splash_star" "eaglette"]
   ["nozomi_yumehara" "yes" "dream"]
   ["rin_natsuki" "yes" "rouge"]
   ["urara_kasugano" "yes" "lemonade"]
   ["komachi_akimoto" "yes" "mint"]
   ["karen_minazuki" "yes" "aqua"]
   ["geki" "juranger" "tirano"]
   ["goushi" "juranger" "manmos"]
   ["dan" "juranger" "torikera"]
   ["boy" "juranger" "tiger"]
   ["mei" "juranger" "ptera"]
   ["burai" "juranger" "dragon"]})

(define $series
  {["precure" "splash_star"]
   ["precure" "futari_ha"]
   ["precure" "yes"]
   ["super_squadron" "juranger"]})

(define $search-series
  (lambda [$series-name]
    (match-all [series warriors] 
        [(set [string string]) (set [string string string])]
      [[<cons [,series-name $season] _> <cons [$name ,season _] _>] 
      [name season]])))

これが,EgisonでもPrologの連言に近い表現ができるようになります.search-series関数を用いれば,シリーズ名からそのシリーズのキャラクター名を列挙することができます.

また,商品と伝票のシステムも考えられると思います.

(define $items {
    ["yotsubato" 500 "comic"]
    ["mario" 5000 "game"]
    ["azumanga" 500 "comic"]
    ["calpis" 140 "water"]
    ["onigiri" 100 "food"]
    ["smash brothers" 5000 "game"]
    ["cola" 140 "water"]
    ["pan" 100 "food"]
    })

(define $sales {
    ["yotsubato" 1]
    ["yotsubato" 2]
    ["calpis" 1]
    ["smash brothers" 1]
    ["cola" 1]
    ["pan" 100]
    ["calpis" 1]
    ["onigiri" 15]
    })

(define $search-genre 
    (lambda [$genre-name] 
        (match-all items 
            (set [string integer string]) 
            [<cons [$name _ ,genre-name]>
            [name]])))

(define $search-sales
    (lambda [$item-name]
        (match-all [items sales]
            [(set [string integer string]) (set [string integer])]
            [[<cons [,item-name $price _] _> <cons [,item-name $sale-nums] _>]
            [price sale-nums]])))

Egisonでも,これらの実装のような方法を用いれば,Prologと同じように推論するようなパターンマッチができるようになります.

Egisonを使った感想

もう少し長い記事だったのですが,Qiitaで編集した内容が消えてしまったので,かなりカットしています.しかも眠いです.修論のベータ版は金曜日に提出なので,それに合わせて適当に書くつもりです.あと5ページぐらいかなと思います.

さて,Egisonを使ってみた感想なのですが,関数型言語は久しぶりで,そこまで深くやっていたわけではなかったのですが,パターンマッチという強力な機能のおかげで,モチベーションを維持することができました.なかなか面白い言語だと思います.

肝心のPrologの連言に近いようなことなのですが,構文がまだ複雑で奥が深いという印象で,書く内容もどうしても長くなる傾向にあると思います.Prologのほうがシンプルで,簡単に質問を投げられることを考えると,Egisonは質問のスキーマを作るための手間が非常に大きいと思います.

ただ,クロールしてきた大量のデータに対してパターンマッチングなどを考える点では,非常に強力な言語だと思います.Prologはその辺はできると思いますが,ガシガシ書ける人が少ない気がします.その点を考慮すると,まだまだ未知数な部分が多く,どこかで成長できる部分がまだ残っているかなと.

パターンマッチングはあくまでEgisonの一つの機能なので,これを使って何をするかが今後重要になってくるんじゃないかなと.最終的な出力系として,これで何が作れるのかというところが,悟りを開く意味で見どころがありそうな言語だと思いました.趣味や遊びの領域をしばらく出そうにはありませんが,もう少しEgisonについて勉強してみたいです.