タグ:SWI-Prolog ( 3 ) タグの人気記事

インターネット広告の「トランスメディア」提供スキンアイコン

フォントの話の続きと見せかけて

フォントの話の続きをかねて、Prologの話題を。

もひとつ、頭を悩ませてくれたのが、バックスラッシュ・円記号問題です。「普通のテキスト」としての運用では、Unicodeテキストとその他のエンコードのテキストとの相互運用で問題が出てきます。こっちはまあ、純粋に円記号という意味の文字が使いたい場合、全角円記号¥を使えばいいだけですので、「日本語処理系」を基準に運用している人はこの回避策が取れます。

問題は、各種プログラミング言語での記号、エスケープ表現、セパレータ記号、などの特殊シンボルとしてバックスラッシュが使われている場合です。といっても、C言語や正規表現ではすでに、円記号を見かけても、それがエスケープ表現なんだと分かる人が多く、混乱することはあまりないと思いますし、ましてやMS OSのディレクトリセパレータは、日本ではむしろ、円記号の方が馴染みがありそうです。ですが、Prologとなると話は別なんですよね。

\=  : not unifiable
\== : not equal
=\= : arithmetic not equal
\+  : logical not
\/  : bit and
/\  : bit or
\   : bit not

これのバックスラッシュを、円記号に置き換えてみると、分かりにくいと思うんじゃないかと。特に、bit and/or は、 ∩ / ∪ の記号をもとに作ってあると思われるので(否定全般がバックスラッシュということから 算術NotEQ もそうしたと思われるけど、これはスラッシュでもいいのにねぇ)。

そこで、このブログで、プログラムコードの例を記述していくにはどうしようか、という悩みが浮上したわけです。結論としては、可能な限り欧文フォントで逃げるのが最良という考えでいきます。

もっとも、今日的なPrologは演算子と数学関数を作るのが自由で、Unicodeソースが扱えるので、それらの記号を定義してしまえ、という考えもアリですかね。以下、SWI-Prologの例で。

∩(X,Y,R):-R is X /\ Y.
∪(X,Y,R):-R is X \/ Y.
:-arithmetic_function(∩/2).
:-arithmetic_function(∪/2).
:-op(500, yfx, ∩).
:-op(500, yfx, ∪).

と書いたソースを読ませておいて、

A is 2 ∩ 3,write(A),nl.

というコードを実行すると、

2

と出てきます。

SWI-Prologって、左右結合と優先順位を含めて、フルに演算子が定義できるので、ある種の式の「記述のスッキリさ」を求めるには悪くないと思います。ただし、算術演算を多数動作させる場合の速度がそんなに早くないので、本格的な「科学技術計算」(膨大な計算によって問題を解くこと)より、事務・業務に独特な演算子を定義して使うとか、簡単な数学の「数学らしい記述」をするためなどに向いていると思います。

また、Windows用バイナリ版として本家に置いてあるものは、多桁小数演算が可能なようにコンパイルされているので、BASICインタプリタを電卓代わりに使っていたことのある人なんかには、関数・演算子の拡張可能な日常的なインタプリタ型の多桁電卓として結構お奨めだったり(BASICよりクセが強いですけど)。

更に、上でなんの断りもなく使っている 'is' は、実は、右辺を算術評価して左辺とUnify(代入みたいなもの)する「演算子」ですが、そうではない評価演算子を独自に定義して使用することで、算術以外でも、たとえば集合演算だとか文字列演算をだとか、更にマニアックなこともできます(あくまで速度よりも「記述面」を求めるために)。まあ、ここで突っ込んでいってもややこしいので、またの機会に。
[PR]

インターネット広告の「トランスメディア」提供スキンアイコン by mikamikanamiyuki | 2009-12-30 00:24 | プログラミング

SWI-Prolog

カテゴリーが違うから分けて連投ー。みたいな。

最近また、SWI-Prolog という Prolog 処理系で、ちょこちょこやってます。

完全に私用の、ちょこっとしたデータベースとして使っているわけですが、その過程で、ちょこちょこと汎用性の高いMy Library述語(他言語で言うところのプロシージャ)が出来上がっていくのが嬉しいです。機会があったら公開したいと思うのですが、まだ先でしょうねー。

Prologは、ある処理が失敗した時に以降に進まない、別候補で再試行する、というのが極自然な処理フローを持つ言語なので、エラー判定・回復処理とかで if 文の山を築く箇所が、だいぶ減るのが嬉しいです。論理型プログラミングとはおおよそ似つかわしくない感想かもしれないけど、高度なことをしなくても利点を享受できるケースもある、と思います。・・・やってる人が高度なことばかりやろうとするから、難しく見えると思うんですよね、アカデミックな言語って。

簡単な例だとこんな感じ。

atom(A),(Prefix='http://';Prefix='ftp://'),sub_atom(A,0,_,_,Prefix),!,何らかの処理.

変数Aのデータ型がatom(文字列みたいな型)で、なおかつ http:// または ftp:// で始まっていたら、「SomeProcess」を行う、というプログラムです。ここではまず、Prefixという変数を http:// に単一化(束縛とか代入とかみたいなもの)してから、AがPrefixの内容で始まっているかテストして、そうじゃなかったら、Prefix に ftp:// を単一化しなおして、もう一回テストしてみる、という処理になります。
; という記号は OR を表すのですが、これは処理フローそのものの「枝分かれ」を意味しているところが、他の処理系で言う論理ORとはずいぶんと違うところです。
! という記号に関しては、カットという仕組みで、面倒なのでめちゃくちゃ大雑把に言うと、「今まで通ってきた道筋は合ってるから、ここより前にはもう戻らないよ!」みたいな宣言です(汗)

atom(A),(sub_atom(A,0,_,_,'http://');sub_atom(A,0,_,_,'ftp://')),!,何らかの処理.
と書いても同じように動作します。こっちのほうが少し平易で、C言語とかに近い感じかも知れません。

他にも、インタープリタ&対話モードを備えている処理系がほぼ100%で、ちょっとしたテストをかますのが簡単だ、ということがあると思います。
例えば、ある言語でRPGのメインプログラムを組むとして、各キャラクターの能力値の種類と、それから算出される実効値や扱いを定義したとします。実際にそのメインのプログラムコードでテストが開始できれば、それでいいのですが、そうでないとき、はたして戦闘シーンのバランスはどんなものか、といったテストをするコードは、多分、Prologでなら、かなり手早く書けると思います(色気のない単なるテキスト表示ですが)。手続き型のプログラミング言語よりも、「仕様データ」を「そのまんまコード化」できる部分が目立つため、慣れにもよりますが、見通し良くしやすいのがポイントです。
場合によっては、少々面倒になると思いますが、「8ターンで終わるくらいのバランス」とか「気を使わないと25%くらいの確率で誰か1人が戦闘不能になるくらい」とか、微妙なバランスを求める逆算も組めると思います。
[PR]

インターネット広告の「トランスメディア」提供スキンアイコン by mikamikanamiyuki | 2009-07-28 01:29 | プログラミング

SWI-Prolog

ごく最近になって、SWI-Prolog
  →  http://www.swi-prolog.org/
という処理系を使ってみています。前々からちょーっと気になっていた、Prolog というプログラミング言語の、フリーの処理系。結構面白いです。

Prolog というのは、論理型言語、という分類のプログラミング言語で、ありていに言えば、「事実」や「規則」をどんどん定義していって、それについて問い合わせる、という形でプログラミングをしていきます…と書くと難しいようですが、手続き型言語ばっかりしか扱ったことのない私にも、意外と違和感なく記述できたりします。

char_to_upper(X,Y):- integer(X),!,(97=<X,X=<122,!,Y is (X-32);Y=X).

簡単なところではこんな感じ。文字コード(Unicode)の半角アルファベット小文字を大文字に変える、という規則の定義です。

C++言語で書くと、以下のに近いです。

bool char_to_upper(int x, int &y)
{
    return 97<=x && x<=122 && ((y=x-32),true) || ((y=x),true);
}

実際には、Prolog版の方は、XにもYにも文字コードを渡してやると、X が大文字化されたものが Y に入っているかを判断して、 true / false で返してくる、という動作も、同じ規則で実行できたりします。

つまり、単一の規則で、

bool char_to_upper(int x, int &y)
{
    return 97<=x && x<=122 && (y==x-32) || (y==x);
}

も担っているということです。

こういった動作を実現できるのは、Prologの変数(自由変数、といいます)が、「ただ一度だけ代入可能」という仕様によるものです。
X=Y / Y=X という記述は、Xが代入済み、Yが代入前の状態だと、X の内容を Y に代入し true を返す。XもYも代入済みの場合、両者が等しければ true を、等しくなければ false を返す。
といった具合に動作します。パターンマッチングという動作のひとつですが、Prologの動作詳細については、別のところを参照いただくとしましてあまり突っ込まずに置いときます。

で、この言語、何でいじっているのかというと、簡易データベース的な動作が簡単にできるな、と思って遊んでみたかったからです。いやあ、最近、色々なソフトでSQLiteが使用されている気がしますが、ちょっとしたもの、例えば、レコード数が5000とかその程度のもので、検索対象文字列が1KB以下、複雑な関係性がないというのなら、この手のものの方が早いんじゃないかな、と。

また、Prologだと、そうした向きにフラットなデータ空間を作った場合、データレコードそのものについては、かなり可読性が高い記述ができますし。

item(b, 1, 0, 'ItemName', 'MainData', ['KeyWord1','KeyWord2']).

これも、Prologのソースになります。

タイプがb
アイテム番号1
所属ディレクトリ番号0
名前
データ
[検索用キーワードのリスト]

といった「事実」の定義です。この程度のデータ構造だったら、人間も読むテキスト形式として
も、HTMLとかXMLよりも簡便なんじゃないかと思います。


ちなみに、問い合わせは、

item(b, Id, Dir, Name, Data, Keyword).

といった具合にやります。タイプが b のものだけを検索してきて、それぞれIdやらDirやらの変数(頭文字が大文字なのが変数として解釈される言語仕様)に、検索されてきたデータが格納されます。ループ処理をすればすべて列挙可能です。
これが、たとえばディレクトリ 0 に属しているもの全てを列挙、データ部分とキーワードは要らない、ということであれば、

item(Type, Id, 0, Name, _, _).

とすれば検索できてしまいます。
[PR]

インターネット広告の「トランスメディア」提供スキンアイコン by mikamikanamiyuki | 2008-09-20 02:01 | プログラミング