カテゴリ:プログラミング( 6 )

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

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

フォントの話の続きをかねて、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 | プログラミング

PowerShellの、パス指定まわりのややこしい話 Get-ChildItem (dir/ls) 編

毎度毎度、PowerShellの話題ばっかりでごめんなさい。

PowerShell では、ワイルドカードが拡張されています。例えば、

foreach($i in 1..9){ni $i.ToString('test_00\.txt') -type file}

とやって、
test_01.txt ~ test_09.txt
までの連番ファイルを作ったとします。その上で、

ls 'test_0[1-3].txt'

などとやると、ちゃんと、

test_01.txt
test_02.txt
test_03.txt

の三つが引っ掛かります。

このように、[]で文字を囲って、
[abc]は、a,b,c のどれか1文字に当てはまる。
[0-9]は、0~9までの1文字に当てはまる。
のような指定が可能になったんですね。
「おー、ちょっと便利だ」と思えなくもないのですが、これが、多大なる悲劇を生んでしまいます。というのも、従来のDOS/Windowsのワイルドカードというのは、
*
?
の2種類。どちらもディレクトリやファイルの名前として使うことが禁止されている文字です。しかし、[]は、ファイル名として使うことが可能なんですね。するとどうか。

foreach($i in 1..9){ni $i.ToString('test[00]\.txt') -type file}

test[01].txt ~ test[09].txt
までの連番ファイルは、ちゃんと作ることができました。
では、さっきみたいに、ls で 01~03 までリストアップしようとすると、あれ、どうすれば…ちょっとややこしそうだというのが想像がつくのではないでしょうか。

結論から言うと、

ls 'test[[]0[1-3]].txt'
# または
ls 'test``[0[1-3]].txt'

となります。

上の方は単純に、[abc]とか[1-3]のような感じで、ワイルドカード文字セットとして [ のみを指定すると
[[]
という、見た目ややこしい感じになる、というだけのことです。

下は…よくは分かりませんけど、lsの -path のワイルドカード文字セットの仕様として、
``
が文字エスケープに該当しているらしいです?
見ればわかるとおり、 ] はエスケープの必要は無いみたいです。

ところで、ファイル名にワイルドカードを使う気がなければ、 -literalpath というパラメータを用いることができます。

ls -literal 'test[01].txt'

しかし、ls -literalpath は、一切のワイルドカードが使えなくなってしまうのが欠点ですね。すなわち、

ls -literal 'test[0?].*'

としても、もちろん、?も*もそのままファイルやディレクトリの名前とみなそうとし、文字が不正である旨のエラーが出力されてしまいます。正直、[]ワイルドカードのみ、抑制するスイッチがあっても良かったと思うんですけどねぇ…。

ちなみに、FileSystem プロバイダ(他にも Registry とか Alias とか Environment とかが存在する)依存で、

ls *.* -filter 'test[0?].*'

とやると、思い通りの動作をするみたいですけど、ここら辺の動作、私はまだあまり分かっていません。すみません。

以上、Get-ChildItem 編は終わりです。実は、これはまだ、多大なる悲劇の序の口です。だって、ちゃんと意識すれば、意図通りの動作はさせられますし、ls の場合は、パイプの起点とすることが多い、オブジェクトを吐きだすためのコマンドレットなので、

ls *.*|?{$_.Name -match 'test\[0[1-3]\]\.txt'}

のようにパイプを使えば、多少動作は重くなりますが、正規表現だって使えますから。





以下、より突っ込んで行きます。


ls -path のエスケープが
``
であるということで、またまた別のややこしい事態が発生します。それは、
` というエスケープ用文字もまた、ファイル名として使える文字であること、です。

'a`a.txt ' は

ls 'a`a.txt'
ls 'a````a*.txt'

でマッチします。

'a``a.txt' はというと、

ls 'a``a.txt'
ls 'a````````a*.txt'

でマッチします。どうやら、
``
がエスケープ文字として扱われるのは、ワイルドカード文字らしきものを含んだ場合のみ、みたいです。逆に言えば、拡張子だけワイルドマッチさせたい場合も、ついうっかり、

ls 'a`a.*'

とやってしまうと、何も引っ掛からなくなってしまう、ということです。

また、 ] はエスケープの必要は無い、と書きましたが、ワイルドカードの文字セットに使いたい場合には、 []abc] のように文字セットの先頭に書くか、 [abc``]] のようにエスケープする必要があります。


PowerShell標準のワイルドカード演算( -like 演算子) のエスケープは、ls と違って、
` 1つだけです。つまり、以下の2つの最終出力は概ね等価。

ls 'test``[0[1-3]].txt'
ls '*.*'|?{$_.Name -like 'test`[0[1-3]].txt'}


-literalpath については、レジストリなどは、項目名として * ? なども受け付ける仕様になっているため、ほとんどのリソースに等価にアクセスできるようにしよう、という設計上、 * ? ともワイルドカード解釈しない、というのは有意なことではあります。


エイリアスはデフォルトで用意されているものを使用していますが、一応、リストアップしておきます。

ni : New-Item
ls : Get-ChildItem
?  : Where-Object


[PR]

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

PowerShellのカレントディレクトリ

PowerShellのカレントディレクトリは、 Get-Location コマンドレットで取得できます。

ただし、 これは見た目上のカレントディレクトリのようで、.NET Framework としてのカレントディレクトリ(本当のカレントディレクトリ)は、 [IO.Directory]::SetCurrentDirectory() を用いて、別途、設定しないとダメみたいです。

if( (Get-Location).Provider.Name -eq 'FileSystem' ){
  [IO.Directory]::SetCurrentDirectory((Get-Location).ProviderPath)
}

追記 :
[IO.Directory]::GetCurrentDirectory()
[IO.Directory]::SetCurrentDirectory()
の代わりに、
[environment]::CurrentDirectory
プロパティを用いても、取得と設定ができるみたいですね。


スクリプトなどでは、ファイルアクセス関連の .NET クラスライブラリを使う前に、こういう感じの文を挿入すべし、ですね(この書き方で、ローカルディレクトリと、Windows/SAMBAネットワークディレクトリに対応)。
ちなみに、else 文を書けば、ファイルシステム以外のドライブ・プロバイダにいる時に例外を飛ばすとかできます。

この仕様、えらく不親切ではあるものの、これはおそらく、PowerShell が、現在のロケーションとして、ファイルシステム以外にも、色々とサポートしていることに起因しているんじゃないかなー、と推測。
でもまあ、不親切ですよね?
[PR]

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

PowerShellのループカウント

PowerShell (v1.0で検証) では、for 文を用いたループカウントより、foreach 文を用いたループカウントの方が、処理速度が上っぽいです。検証用の一行スクリプトを例示すると、


#for を用いたループカウント

[long]$sum=0;$t_start=(Get-Date);for([int]$i=1;$i -le 100000;++$i){$sum+=$i;};((Get-Date)-$t_start).TotalSeconds


上記よりも、


#foreachを用いたループカウント

[long]$sum=0;$t_start=(Get-Date);foreach($i in 1..100000){$sum+=$i;};((Get-Date)-$t_start).TotalSeconds


とした方が時間がかかりません。

しかも、カウンタ変数を、シェルやスクリプトのどこかでそれ以前に、[int]以外の型、例えば [double] 型などにしていて、それをそのまま使ってしまうと、for 文ではそのまま [double] として扱われ続けますが、foreach 文だと、ちゃんと[int]型として参照するので、不意のパフォーマンスダウンも避けられます。

ちなみに、foreach 文で使われた参照変数は、その後に何かを代入すると、もともと宣言されていた型に戻ります。

例えば、


[double]$a=0.0
foreach($a in 1..10){}

$a.GetType()
#ここでは Int32 型([int]型と同じ)と出る

$a = [int]1 #念入りに強制[int]型キャストで代入してみる
$a.GetType()
#ここでは Double 型と出る

[PR]

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