前の記事の答え(または黒魔術)


文中のプログラムは foo/1 の節が連続していないところに問題がある.処理系によっては何事もなく読み込まれるが,処理系によっては警告が出る.それでも,「警告はエラーにあらず」と言い張れば,ほとんどの処理系では実用上問題ないが,GNU Prolog だけは最初の節から連続していないものは無視される仕様になっており,そのため go/0 が失敗する.

実は Prolog にも ISO の標準規格なるものが(一応)存在して,ずばり「節が連続していない」ことを示す discontiguous という宣言が定義されている.すると,

:- discontiguous(foo/1).

と書けば話が済みそうなのだが,残念ながら XSB にはこれが実装されておらずエラーになる*1.処理系によっては foo/1 を動的述語*2にすれば警告が消える,といった裏技があるが,肝心の GNU Prolog では discontiguous 宣言を入れない限りは後ろの節が無視されるため,上記の宣言はどうしても必要になる.ちなみに,たいていの処理系では foo/1 を囲む括弧は必要ないが,GNU Prolog だけは括弧をつけないとエラーになる.

XSB は :- で始まる行のうち宣言*3にあたるもの以外は query とみなしてプログラムの読み込み時に評価されることになっているので,discontiguous/1 という述語のダミーを用意してごまかすことはできる.とはいえ,

discontiguous(_).

と書けば済むかというと,XSB では確かに動作するようになるが,今度は YAP が組み込み述語を再定義するなと文句を言ってくる.まあ,非標準ながら Prolog にも条件付きコンパイルのような機能があって,たとえば,

:- if current_predicate(discontiguous/1).
discontiguous(_).
:- endif.

のように書けたりもするのだが,Ciao にはこの機能がないため困ったことになる.

いよいよ手詰まりのように見えるが,実はもうひとつだけ手段が残っている.ほとんどの処理系で dynamic/1 をはじめとする各種宣言は組み込み述語になっていて,宣言以外のところでも他の述語と同じように呼び出すことができる*4.そもそも,先の YAP のエラーはこれが原因なのであった.

これを利用すると discontiguous/1 を動的述語として「初期化時に」定義するという芸当ができる.少々ややこしいロジックがいるので,補助述語を定義することにする.

:- initialization(define_discontiguous).

define_discontiguous :-
    current_prolog_flag(dialect,xsb), !,
    dynamic(discontiguous/1),
    assertz(discontiguous(_)).
define_discontiguous.

initialization(Goal) は初期化時に Goal を実行するという意味で,これは ISO の標準規格で定義されており,またどの処理系でも使える.XSB などでは単に Goal の部分だけを書いても普通に評価される,というのは最初のほうで触れたとおりだが,GNU Prolog では :- の後ろには宣言しか書けないので,上のとおりにする必要がある.

さて,これで完成,と言いたいところだが,ここに来て SWI-Prolog に妨害される.というのも,どういうわけか dynamic(discontiguous/1) のところで parser が混乱するらしく syntax error だと言われてしまう.とはいえ,これは適当に obfuscate すれば回避できる.

というわけで,問題の答えは次のとおりである.

:- initialization(define_discontiguous).

define_discontiguous :-
    current_prolog_flag(dialect,xsb), !,
    atom_chars(Name,[d,i,s,c,o,n,t,i,g,u,o,u,s]),
    dynamic(Name/1),
    assertz(discontiguous(_)).
define_discontiguous.

:- discontiguous(foo/1).

まあ,まさに誰得な記事である.

*1:一応,本体部分は読み込まれるので go/0 自体は正しく動作する.

*2:要するに :- dynamic(foo/1). を追加する.

*3:要するに dynamic とか include とか.

*4:問題文中にあげたものだと GNU Prolog 以外は普通の述語として呼び出せる.ほかの有名どころだと SICStus は呼び出し不可.

Prolog

問. 以下のプログラムについて,述語 go/0 の呼び出しが次の処理系の全部で成功するように,プログラムの先頭に適切なコードを追加しなさい.なお,プログラムの読み込み時または実行時にエラーが発生しないようにすること.また,foo/1,bar/1,go/0 の定義および節の順番を変更してはならない.
CiaoGNU PrologSWI-PrologXSBYAP

foo(x).
bar(y).
foo(z).

go :- findall(X,foo(X),Xs), Xs == [x,z].

答えは後日.

実験の片付けをさぼったら世界的快挙

 茨城県の女子高生らが新たな化学現象を発見し、権威のある米専門誌に論文が掲載されることが決まった。
  (中略)
 その日、水溶液の色は想定通り赤で動かなくなった。メンバーは器具を片付けないままカラオケへ。ところが月曜日に実験室に戻ると、液は黄色くなっていた。
 予想外のことで、観察を繰り返した結果、赤青の変化が一度止まった後、突然、始まった。全く知られていない現象だったが、試薬の条件が整えば、5〜20時間後に変化が再開することを突き止めた。
部活リケジョ、「化学」大発見、米誌に掲載へ

何でも片付ければいいというものでもないらしい.茨城は(もとの)地元なのでこの快挙はとても誇らしい.

ドライバ不要

ラップトップから取り外された 2.5-inch HDD を接続するためにこのハードディスクケースを購入したのだが,箱に「ドライバ不要の簡単接続」と銘打たれているにもかかわらず,リンク先のページの説明にあるとおり箱にはドライバーが入っていた.確かにねじ締め不要とはどこにも書かれていなかった.

ところで,これを eSATA で接続して起動したところ,そのハードディスクの中にある Windows がこともなげに起動した.ドライバの変更のために一度再起動する必要はあったものの,あとは特に問題ないように見える.わお.