<?xml version="1.0" encoding="UTF-8"?>

<rdf:RDF
  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:admin="http://webns.net/mvcb/"
  xmlns:content="http://purl.org/rss/1.0/modules/content/"
  xmlns="http://purl.org/rss/1.0/"
>

<channel rdf:about="http://rainyday.blog.so-net.ne.jp/">
<title>Rainy Day Codings</title>
<link>http://rainyday.blog.so-net.ne.jp/</link>
<description>雨の日のプログラミング</description>
<items>
<rdf:Seq>
<rdf:li rdf:resource="http://rainyday.blog.so-net.ne.jp/2009-11-13" />
<rdf:li rdf:resource="http://rainyday.blog.so-net.ne.jp/2009-10-01" />
<rdf:li rdf:resource="http://rainyday.blog.so-net.ne.jp/2009-05-06" />
<rdf:li rdf:resource="http://rainyday.blog.so-net.ne.jp/2009-05-03" />
<rdf:li rdf:resource="http://rainyday.blog.so-net.ne.jp/2009-01-04" />
<rdf:li rdf:resource="http://rainyday.blog.so-net.ne.jp/2008-12-13" />
<rdf:li rdf:resource="http://rainyday.blog.so-net.ne.jp/2008-10-25" />
<rdf:li rdf:resource="http://rainyday.blog.so-net.ne.jp/2008-10-03" />
<rdf:li rdf:resource="http://rainyday.blog.so-net.ne.jp/2008-09-27" />
<rdf:li rdf:resource="http://rainyday.blog.so-net.ne.jp/2008-07-09" />
<rdf:li rdf:resource="http://rainyday.blog.so-net.ne.jp/2008-06-16" />
<rdf:li rdf:resource="http://rainyday.blog.so-net.ne.jp/2008-06-03" />
<rdf:li rdf:resource="http://rainyday.blog.so-net.ne.jp/2008-05-13" />
<rdf:li rdf:resource="http://rainyday.blog.so-net.ne.jp/2008-05-12" />
<rdf:li rdf:resource="http://rainyday.blog.so-net.ne.jp/2008-05-11-1" />
<rdf:li rdf:resource="http://rainyday.blog.so-net.ne.jp/2008-05-11" />
<rdf:li rdf:resource="http://rainyday.blog.so-net.ne.jp/2008-05-09" />
<rdf:li rdf:resource="http://rainyday.blog.so-net.ne.jp/2008-05-07" />
<rdf:li rdf:resource="http://rainyday.blog.so-net.ne.jp/2008-05-05-1" />
<rdf:li rdf:resource="http://rainyday.blog.so-net.ne.jp/2008-05-05" />
</rdf:Seq>
</items>

<dc:creator>ether</dc:creator>
<dc:date>2009-11-13T22:59:35+09:00</dc:date>
<dc:language>ja</dc:language>
</channel>

<item rdf:about="http://rainyday.blog.so-net.ne.jp/2009-11-13">
<title>ウクレレのコードを見つけるプログラム</title>
<link>http://rainyday.blog.so-net.ne.jp/2009-11-13</link>
<description>ウクレレやギターのようなコードを弾く弦楽器では同じコードを弾くのにも何通りもの押さえ方が存在します。ウクレレで C のコード（C, E, G の3音）を鳴らす場合を例にとりましょう。ウクレレには4本の弦があり、何も押さえない状態では1弦から4弦まで順に A, E, C, G の4つの音を出します。A の音はCコードの構成音ではないので1弦の3フレットを押さえます。これにより1弦から出る音は A から3つ (A-&amp;gt;Bb-&amp;gt;B-&amp;gt;C) シャープした C の音になって C, E, C, G の音が出るわけです。一方で1弦7フレットを押さえても C のコードと見なせます。A から7つ分シャープすると A-&amp;gt;Bb-&amp;gt;B-&amp;gt;C-&amp;gt;C#-&amp;gt;D-&amp;gt;Eb-&amp;gt;E となり、鳴る音は E, E, C, G となります。これも C, E, G で構成されるのでCのコードと言えるのです。このような何通りもの押さえかたは一番高い音を何にしたいかといった理由などで使い分けますが、市販の教則本等のコード一覧に押さえ方としても載っているのはそのうちの代表的な1つか、コードブック的なもので各々3つくらいです。タブ譜などを見ながら押さえ方の難しいコードが出てきて「もうちょっと楽な押さえ方はないのか？」というときに自分で編み出すのはなかなか大変ですので自動で計算するプログラムを OCaml で書いてみました。まずは型定義です。音名をヴァリアント型で定義します。type tone = C | Db | D | Eb | E | F | Gb | G | Ab | A | Bb | Bこれは整数で表現したほうが演算（シャープするとかフラットするとか）ができていいという考え方もあるかもしれませんが、今回のプログラムでは特に不要だったのでヴァリアント型にしました。異名同音（C#とDbとか）はすべてフラットのほうの書き方にしていますが、これはヴァリアント型のコンストラクタに記号が使えないためです。次にいくつかタイプエイリアスを定義します。type chord = tone listtype position = tone * inttype string_ = position listtype form = position listtype state = form * string_ list実を言..</description>
<dc:subject>OCaml</dc:subject>
<dc:creator>ether</dc:creator>
<dc:date>2009-11-13T22:59:35+09:00</dc:date>
<content:encoded><![CDATA[
<p>ウクレレやギターのようなコードを弾く弦楽器では同じコードを弾くのにも何通りもの押さえ方が存在します。
</p>
<p>ウクレレで C のコード（C, E, G の3音）を鳴らす場合を例にとりましょう。ウクレレには4本の弦があり、何も押さえない状態では1弦から4弦まで順に A, E, C, G の4つの音を出します。A の音はCコードの構成音ではないので1弦の3フレットを押さえます。これにより1弦から出る音は A から3つ (A-&gt;Bb-&gt;B-&gt;C) シャープした C の音になって C, E, C, G の音が出るわけです。
</p>
<p>一方で1弦7フレットを押さえても C のコードと見なせます。A から7つ分シャープすると A-&gt;Bb-&gt;B-&gt;C-&gt;C#-&gt;D-&gt;Eb-&gt;E となり、鳴る音は E, E, C, G となります。これも C, E, G で構成されるのでCのコードと言えるのです。
</p>
<p>このような何通りもの押さえかたは一番高い音を何にしたいかといった理由などで使い分けますが、市販の教則本等のコード一覧に押さえ方としても載っているのはそのうちの代表的な1つか、コードブック的なもので各々3つくらいです。タブ譜などを見ながら押さえ方の難しいコードが出てきて「もうちょっと楽な押さえ方はないのか？」というときに自分で編み出すのはなかなか大変ですので自動で計算するプログラムを OCaml で書いてみました。
</p>
<p>まずは型定義です。音名をヴァリアント型で定義します。
</p>
<pre>type tone = C | Db | D | Eb | E | F | Gb | G | Ab | A | Bb | B
</pre>
<p>これは整数で表現したほうが演算（シャープするとかフラットするとか）ができていいという考え方もあるかもしれませんが、今回のプログラムでは特に不要だったのでヴァリアント型にしました。異名同音（C#とDbとか）はすべてフラットのほうの書き方にしていますが、これはヴァリアント型のコンストラクタに記号が使えないためです。
</p>
<p>次にいくつかタイプエイリアスを定義します。
</p>
<pre>type chord = tone list
type position = tone * int
type string_ = position list
type form = position list
type state = form * string_ list
</pre>
<p>実を言うと実際に後で型注釈とかをして使うわけではないのですが、一応気分の問題で定義しておきました。コード chord は音名を集めたものです。ポジション position は何フレット目を押さえると何の音が出るかを示しています。弦 string_ はポジションを1列に並べたものです。フォーム form は各弦のどのポジションを押さえてコードを鳴らすかを表現します（要素数4つのリストです）。state がよくわからないと思いますが、これは後で説明します。以下、型を説明するときはこれらを使います。
</p>
<p>次にウクレレの4つの弦を表現するデータを定義します。
</p>
<pre>let rec iota m n = if m = n then [n] else m :: iota (m + 1) n
let a_string = List.combine [A; Bb; B; C; Db; D; Eb; E; F; Gb; G; Ab] (iota 0 11)
let e_string = List.combine [E; F; Gb; G; Ab; A; Bb; B; C; Db; D; Eb] (iota 0 11)
let c_string = List.combine [C; Db; D; Eb; E; F; Gb; G; Ab; A; Bb; B] (iota 0 11)
let g_string = List.combine [G; Ab; A; Bb; B; C; Db; D; Eb; E; F; Gb] (iota 0 11)
let strings = [a_string; e_string; c_string; g_string]
</pre>
<p>これは以下のような構造を作っています。(note * int) list list は気分としては string_ list です。
</p>
<pre># strings;;
- : (note * int) list list =
[[(A, 0); (Bb, 1); (B, 2); (C, 3); (Db, 4); (D, 5); (Eb, 6); (E, 7); (F, 8); (Gb, 9); (G, 10); (Ab, 11)];
 [(E, 0); (F, 1); (Gb, 2); (G, 3); (Ab, 4); (A, 5); (Bb, 6); (B, 7); (C, 8); (Db, 9); (D, 10); (Eb, 11)];
 [(C, 0); (Db, 1); (D, 2); (Eb, 3); (E, 4); (F, 5); (Gb, 6); (G, 7); (Ab, 8); (A, 9); (Bb, 10); (B, 11)];
 [(G, 0); (Ab, 1); (A, 2); (Bb, 3); (B, 4); (C, 5); (Db, 6); (D, 7); (Eb, 8); (E, 9); (F, 10); (Gb, 11)]]
</pre>
<p>ここでは11フレットまでしか用意しませんでしたが、もちろんもっと用意してもかまいません。
</p>
<p>ユーティリティ関数として (A, 0) などのポジションと [C; E; G] というコードが与えられたときに A がコード [C; E; G] の構成音かどうかを判定する関数を用意しておきます。position -&gt; chord -&gt; bool です。
</p>
<pre>let is_chord_tone (tone, _) chord = List.mem tone chord
</pre>
<p>今回のコード探索プログラムは「クロージャを作成して、そのクロージャを呼び出すたびにコードフォームの候補を次々返してくれる」というものにします。そのクロージャは unit -&gt; form で、呼び出す度に [(G, 0); (C, 0); (E, 0); (C, 3)] とか [(G, 0); (C, 0); (E, 0); (E, 7)] を返してくれるイメージです。
</p>
<p>クロージャを作り出すための関数を次のように定義しました。
</p>
<pre>let create_form_finder strings chord_to_find filters =
  let agenda = Queue.create () in 
  let rec find () =
    let state = Queue.take agenda in
    match state with
    | (form, []) -&gt; if List.for_all (fun p -&gt; p form) filters then form else find ()
    | (form, (position :: string) :: strings) -&gt;
        if (is_chord_tone position chord_to_find) then
          (Queue.push (position :: form, strings) agenda;
           Queue.push (form, string :: strings) agenda;
           find ())
        else
          (Queue.push (form, string :: strings) agenda;
           find ())
    | (_, [] :: _) -&gt; find ()
  in
  Queue.push ([], strings) agenda;
  find
</pre>
<p>まず引数です。
</p>
<pre>let create_form_finder strings chord_to_find filters =
</pre>
<p>strings : string_ list には先ほど定義した strings を与えます。変則チューニングにも対応できるように引数にしました。chord_to_find : chord は見つけたいコードです。filters : (form -&gt; bool) list は検索するコードフォームをフィルタリングする条件を与えます。複数指定できるようにリストにしました。
</p>
<pre>  let agenda = Queue.create () in
</pre>
<p>agenda : state Queue.t には探索中の状態が入ります。state は type state = form * string_ list と定義しましたが「これまでに押さえたポジション * まだ押さえていない弦」という意味になります。
</p>
<pre>  let rec find () =
    let state = Queue.take agenda in
    match state with
</pre>
<p>コード探索クロージャの中ではまず agenda から状態をひとつとって、その状態の内容で進み方を決めます。
</p>
<p>まず「すでに全ての弦を押さえていて、コードフォームになっている」という場合です。
</p>
<pre>    | (form, []) -&gt; if List.for_all (fun p -&gt; p form) filters then form else find ()
</pre>
<p>この場合、フィルタリング条件を適用して OK だったらコードフォームを返します。だめだったら探索を続けます。
</p>
<p>次のケースはまだ全ての弦を押さえていない途中のケースです。
</p>
<pre>    | (form, (position :: string) :: strings) -&gt;
</pre>
<p>このケースは「残りの最初の弦の一番低いポジション」がコードの構成音かどうかで場合分けします。
</p>
<pre>        if (is_chord_tone position chord_to_find) then
          (Queue.push (position :: form, strings) agenda;
           Queue.push (form, string :: strings) agenda;
           find ())
</pre>
<p>構成音だった場合「そのポジションを押さえる」か「そのポジションは押さえずにもっと高いポジションを押さえるか」という2通りの行き先があります。これは先の C コードの例で言うと1弦3フレットが構成音だけど3フレットを押さえるか、もっと先（7フレット）を押さえるかというようなことです。1つ目の push は押さえるほうの分岐で、フォームにポジションを追加して残りの弦を減らします。2つ目の push は押さえない判断で、フォームはそのままで弦の最低ポジションを捨てます（次のフレットが次回 agenda から取られるときの最低ポジションになります）。
</p>
<pre>        else
          (Queue.push (form, string :: strings) agenda;
           find ())
</pre>
<p>構成音ではなかった場合はフォームはそのままで弦の最低ポジションを捨てます。
</p>
<pre>    | (_, [] :: _) -&gt; find ()
</pre>
<p>今回の定義では各弦について11フレット目までしかポジションを定義していないので、最低ポジションを捨て続けて11フレットまでを使い切ったら「詰み」になります。
</p>
<pre>  in
  Queue.push ([], strings) agenda;
  find
</pre>
<p>初期状態として「まだどこも押さえていなくて4弦すべて残っている状態」をキューに入れ、クロージャを返しています。
</p>
<p>とりあえずこの状態で使ってみましょう。
</p>
<pre># let find = create_form_finder strings [C; E; G] [];;
val find : unit -&gt; (note * int) list = &lt;fun&gt;
# find ();;
- : (note * int) list = [(G, 0); (C, 0); (E, 0); (C, 3)]
# find ();;
- : (note * int) list = [(G, 0); (C, 0); (G, 3); (C, 3)]
# find ();;
- : (note * int) list = [(G, 0); (E, 4); (E, 0); (C, 3)]
# find ();;
- : (note * int) list = [(G, 0); (C, 0); (E, 0); (E, 7)]
</pre>
<p>1弦3フレットを押さえる C や 1弦7フレットを押さえる C を見つけてくれています。一方で2つ目の結果のように C と G だけで E が入っていないフォームも結果に含まれています。「完全なコード」だけを検出するように追加条件を指定したいと思います。
</p>
<pre>let is_complete_chord chord form = 
  let tone_included tone = List.exists (fun (t, _) -&gt; t = tone) form in
  List.for_all tone_included chord
</pre>
<p>これを指定すると次のような結果になります。
</p>
<pre># let find = create_form_finder strings [C; E; G] [is_complete_chord [C; E; G]];;
val find : unit -&gt; (note * int) list = &lt;fun&gt;
# find ();;
- : (note * int) list = [(G, 0); (C, 0); (E, 0); (C, 3)]
# find ();;
- : (note * int) list = [(G, 0); (E, 4); (E, 0); (C, 3)]
# find ();;
- : (note * int) list = [(G, 0); (C, 0); (E, 0); (E, 7)]
</pre>
<p>不完全なコードを除外してくれるようになりました。なお「最初から不完全なコードを除外するように create_chord_finder を書けばいいのでは？」と思うかもしれませんが、ウクレレでは不完全なコードを使うことも結構あるため（例えば D7=D+F#+A+C のコードに対して A+F#+C+A で押さえることも多い）、追加条件で指定する仕様にしました。
</p>
<p>ところで、コード検出を続行すると次のようなフォームを検出します。
</p>
<pre># find ();;
- : (note * int) list = [(G, 0); (G, 7); (E, 0); (C, 3)]
# find ();;
- : (note * int) list = [(G, 0); (E, 4); (G, 3); (C, 3)]
# find ();;
- : (note * int) list = [(G, 0); (C, 0); (G, 3); (E, 7)]
# find ();;
- : (note * int) list = [(G, 0); (C, 0); (E, 0); (G, 10)]
# find ();;
- : (note * int) list = [(C, 5); (G, 7); (E, 0); (C, 3)]
# find ();;
- : (note * int) list = [(E, 9); (C, 0); (G, 3); (C, 3)]
</pre>
<p>3フレット目と9フレット目を同時に押さえるというのは無理ですね。3と7もすこし遠いかもしれません。押弦している最低フレットと最高フレットの差を3フレットまでに限定してみます。
</p>
<pre>let is_possible_form form =
  let non_open = List.filter (fun (_, fret) -&gt; fret &lt;&gt; 0) form in
  match non_open with
  | [] -&gt; true
  | _ :: [] -&gt; true
  | (_, fret) :: ps -&gt;
      let (min_, max_) =
        List.fold_left
          (fun (min_, max_) (_, fret) -&gt; (min min_ fret, max max_ fret))
          (fret, fret)
          ps
      in
      (max_ - min_) &lt; 4
</pre>
<p>この条件を追加すると11フレット目までの全ての C コードは以下のとおりとなります。
</p>
<pre># let find = create_form_finder strings [C; E; G] [is_complete_chord [C; E; G]; is_possible_form];;
val find : unit -&gt; (note * int) list = &lt;fun&gt;
# find ();;
- : (note * int) list = [(G, 0); (C, 0); (E, 0); (C, 3)]
# find ();;
- : (note * int) list = [(G, 0); (E, 4); (E, 0); (C, 3)]
# find ();;
- : (note * int) list = [(G, 0); (C, 0); (E, 0); (E, 7)]
# find ();;
- : (note * int) list = [(G, 0); (E, 4); (G, 3); (C, 3)]
# find ();;
- : (note * int) list = [(G, 0); (C, 0); (E, 0); (G, 10)]
# find ();;
- : (note * int) list = [(C, 5); (E, 4); (G, 3); (C, 3)]
# find ();;
- : (note * int) list = [(G, 0); (C, 0); (C, 8); (E, 7)]
# find ();;
- : (note * int) list = [(C, 5); (G, 7); (E, 0); (E, 7)]
# find ();;
- : (note * int) list = [(E, 9); (C, 0); (E, 0); (G, 10)]
# find ();;
- : (note * int) list = [(G, 0); (G, 7); (C, 8); (E, 7)]
# find ();;
- : (note * int) list = [(C, 5); (G, 7); (C, 8); (E, 7)]
# find ();;
- : (note * int) list = [(E, 9); (C, 0); (C, 8); (G, 10)]
# find ();;
- : (note * int) list = [(E, 9); (G, 7); (C, 8); (E, 7)]
# find ();;
- : (note * int) list = [(E, 9); (G, 7); (C, 8); (G, 10)]
# find ();;
Exception: Queue.Empty.
</pre>
<a name="more"></a>
]]></content:encoded>
</item>
<item rdf:about="http://rainyday.blog.so-net.ne.jp/2009-10-01">
<title>「はい/いいえ」、「○/×」、「真/偽」</title>
<link>http://rainyday.blog.so-net.ne.jp/2009-10-01</link>
<description>ある会社の経理担当と「中小企業の会計に関する指針」の適用に関するチェックリストのことで大喧嘩である。「営業上の債権のうち破産債権等で１年以内に弁済を受けることができないものがある場合、これを投資その他の資産の部に表示したか。」という確認事項があって、○か×かでチェックを入れなければならない。その会社には「営業上の債権のうち破産債権等で１年以内に弁済を受けることができないもの」が無かった。（中略）だからここは「○」にするのが正しいと私はその会社の経理担当に言った。http://d.hatena.ne.jp/yaneurao/20090927#p1自然言語のシンタクスを論理学の意味論を使って（構文要素を対応させて）解釈すると自然言語の話し手が意図し（また聞き手が解釈し）ている意味とずれが生じていることがある（だがそのずれには何らかの法則があるはずだからそれを解明しよう）―というのが言語学的語用論の出発点だ。そういう例はたくさんある。例えば自然言語の &quot;or&quot; や「または」は論理和と解釈される場合と排他的論理和と解釈される場合がある、とか。上記もそうした例の一つといえる。面白い例なので私も少し考えてみたところ、問題は自然言語（日本語）の「はい/いいえ」が論理学の「真/偽」とは異なっていること、そして「○/×」が状況によっては「はい/いいえ」とほぼ同義なものとして使われていること（だがそう感じない人もいるようであること）にあるのではないかと思った。（ちなみにこのトラブルで誰に非があるかという裁定には興味はない）例文を少しわかりやすいものに変えたい。あなたは家を出るときに忘れ物をすることが多いので、これを防止するために外出時のチェックシートを作って運用することに決めた、ということにしよう。チェックシートにはチェック欄があり、チェック欄すべてを「レ」で埋めたら玄関を出ることができる。家を出る前に以下をチェックせよ・携帯電話を持った　（　）・財布を持った　（　）・雨が降っている場合、傘を持った　（　）携帯を持ち、財布をもち、雨が降っておらず、傘を持っていない場合、すべての空欄に「レ」を付けることに殆どの人は躊躇しないだろう。何しろ全てチェックしないと出かけられないわけだし。携帯を持ち、財布をもち、雨が降っておらず、しかし傘を持っている場合も「レ」をつけるだろう。雨が降っていて傘を持った場合も。しかし雨が降っていて傘を持っていな..</description>
<dc:subject>自然言語</dc:subject>
<dc:creator>ether</dc:creator>
<dc:date>2009-10-02T00:19:02+09:00</dc:date>
<content:encoded><![CDATA[
<blockquote>ある会社の経理担当と「中小企業の会計に関する指針」の適用に関するチェックリストのことで大喧嘩である。<br/>
<br/>
「営業上の債権のうち破産債権等で１年以内に弁済を受けることができないものがある場合、これを投資その他の資産の部に表示したか。」という確認事項があって、○か×かでチェックを入れなければならない。<br/>
<br/>
その会社には「営業上の債権のうち破産債権等で１年以内に弁済を受けることができないもの」が無かった。<br/>
（中略）<br/>
だからここは「○」にするのが正しいと私はその会社の経理担当に言った。<br/>
<a href="http://d.hatena.ne.jp/yaneurao/20090927#p1" target="_blank">http://d.hatena.ne.jp/yaneurao/20090927#p1</a></blockquote>

<p>自然言語のシンタクスを論理学の意味論を使って（構文要素を対応させて）解釈すると自然言語の話し手が意図し（また聞き手が解釈し）ている意味とずれが生じていることがある（だがそのずれには何らかの法則があるはずだからそれを解明しよう）―というのが言語学的語用論の出発点だ。そういう例はたくさんある。例えば自然言語の "or" や「または」は論理和と解釈される場合と排他的論理和と解釈される場合がある、とか。

</p><p>上記もそうした例の一つといえる。面白い例なので私も少し考えてみたところ、問題は自然言語（日本語）の「はい/いいえ」が論理学の「真/偽」とは異なっていること、そして「○/×」が状況によっては「はい/いいえ」とほぼ同義なものとして使われていること（だがそう感じない人もいるようであること）にあるのではないかと思った。（ちなみにこのトラブルで誰に非があるかという裁定には興味はない）</p>

<p>例文を少しわかりやすいものに変えたい。あなたは家を出るときに忘れ物をすることが多いので、これを防止するために外出時のチェックシートを作って運用することに決めた、ということにしよう。チェックシートにはチェック欄があり、チェック欄すべてを「レ」で埋めたら玄関を出ることができる。</p>

<p>家を出る前に以下をチェックせよ<br/>
・携帯電話を持った　（　）<br/>
・財布を持った　（　）<br/>
・雨が降っている場合、傘を持った　（　）</p>

<p>携帯を持ち、財布をもち、雨が降っておらず、傘を持っていない場合、すべての空欄に「レ」を付けることに殆どの人は躊躇しないだろう。何しろ全てチェックしないと出かけられないわけだし。携帯を持ち、財布をもち、雨が降っておらず、しかし傘を持っている場合も「レ」をつけるだろう。雨が降っていて傘を持った場合も。しかし雨が降っていて傘を持っていなければ傘を手にするまで「レ」を付けないに違いない。つまり我々はここで「論理学的」に考えている。「雨が降っている場合、傘を持ったか」は「P→Q」の解釈と同じで、「レ」は「真」を意味する。</p>

<p>続けて次の例を考えよう。あなたは家を出るときに忘れ物をすることが多いので、外出時に同居人に確認の質問を毎回してもらうように頼むことにした。あなたは携帯を持っており、財布を持っており、雨は降っておらず、傘を持っていないとしよう。同居人は布団の中から声をかけており、外で雨が降っているかどうか知らない。</p>

<p>同居人「携帯電話は持った？」　あなた「はい」<br/>
同居人「財布は持った？」　あなた「はい」<br/>
同居人「雨だったら傘は持った？」</p>

<p>多分最後の質問に対する答えは「いや（傘は持っていない）。でも雨はふっていないから」とか、「いや、雨は降っていない（だから傘を持つ必要はない）」になるだろう。おそらく殆どの日本語話者の言語的直観はこの状況で単に「はい」と答えることを自然と感じないはずだ。もし答えるとしたら、それは忙しい朝のやり取りを短く終わらせるために無害な嘘をついてみた（「はいはい」）といったところだろう。</p>

<p>では先のチェックシートが以下のような形をしており、「レ」ではなく｢○/×」を記入する運用だった場合はどうか。</p>

<p>・携帯電話を持ったか　（○・×）<br/>
・財布を持ったか　（○・×）<br/>
・雨が降っている場合、傘を持ったか（○・×）</p>

<p>雨が降っていなくて傘を持っていないときに「○」を付けることに、さっきより違和感を感じるのではないだろうか。このことは「○/×」が「真/偽」よりは「はい/いいえ」に近いという認識からくるものだと思う(*1)。「持ったか」という疑問文にしたことでその解釈への志向も強まっている。</p>

<p>最後に元の例に戻ろう。この例は自作の忘れ物防止チェックシートとは異なり、他人に対して回答するものである（と思う。たぶん）。この状況は先ほどの同居人の例にさらに近づく。そして「○」により強い違和感を覚えるのがたいていの人の反応だろうと思う。「○/×」でなく「はい/いいえ」で回答する方式だったらさらに違和感は増すのではないだろうか。</p>

<p>以上のまとめ。対話的状況での「はい」は条件節を含む疑問文の後件を肯定する強い傾向がある。次のスケールで左に行くほど成立状況が限定される。</p>

<p>　はい　＜　○　＜　レ<br/>
　対話　＜　非対話</p>

<p>*1 「はい/いいえ」よりも解釈に個人差があるとしたら、「はい/いいえ」は通常の日本語の語彙として自然に習得するのに対して「○/×」はもっと「後天的」に習得しているからかもしれない。</p><a name="more"></a>
]]></content:encoded>
</item>
<item rdf:about="http://rainyday.blog.so-net.ne.jp/2009-05-06">
<title>Semantics with Applications を読んで denotational semantics について知る (2)</title>
<link>http://rainyday.blog.so-net.ne.jp/2009-05-06</link>
<description>先に進む前に、前回みた while 文の意味の定義を振り返る。</description>
<dc:subject>未分類</dc:subject>
<dc:creator>ether</dc:creator>
<dc:date>2009-05-06T11:50:55+09:00</dc:date>
<content:encoded><![CDATA[
<p>先に進む前に、前回みた while 文の意味の定義を振り返る。</p><a name="more"></a><p>文の semantic function から while 文のところだけを抜き出すと、</p>

<pre>| While(condition, stm1) ->
    let f g = cond (b condition) (g $ (s stm1)) id in
    fix f</pre>

<p>だった。ただし fix f は「f g = g になるような g」を返すものとする。この定義がそもそもどうやって出てくるのかを見ていきたい。まずは次のことがいえる。</p>

<p>「while b do S は if b then (S; while b do S) else skip と書き換えてもよい。」</p>

<p>これはどうやらその通りだ。というわけで、</p>

<pre>| While(condition, stm1) ->
    s Cond(condition, Seq(stm1, While(condition, stm1)), Skip)</pre>

<p>とする。条件文に対する semantic function の定義は、</p>

<pre>| Cond(condition, stm1, stm2) -> cond (b condition) (s stm1) (s stm2)</pre>

<p>だったから、</p>

<pre>| While(condition, stm1) ->
    cond (b condition) (s Seq(stm1, While(condition, stm1))) (s Skip)</pre>

<p>となる。順次実行の定義は、</p>

<pre>| Seq(stm1, stm2) -> (s stm2) $ (s stm1) </pre>

<p>だったから、</p>

<pre>| While(condition, stm1) ->
    cond (b condition) ((s While(condition, stm1)) $ (s stm1)) (s Skip)</pre>

<p>となる。NOPの定義は、</p>

<pre>| Skip -> id</pre>

<p>だったから、さらに、</p>

<pre>| While(condition, stm1) ->
    cond (b condition) ((s While(condition, stm1)) $ (s stm1)) id</pre>

<p>となる。これはまだ、s While(condition, stm1) の内容を定義するために s While(condition, stm1) の全体を必要としているから、「統語範疇の意味はその構成要素だけから」決まるようにするという denotational semantics の目標に達していない。</p>

<p>ところでこの節は s の中の While に関する定義なのだから、必要とされる「s While(condition, stm1)」は「cond (b condition) (s While(condition, stm1)) $ (s stm1)) id」の戻り値でもある。</p>

<p>これを頭において、</p>

<pre>cond (b condition) ((s While(condition, stm1)) $ (s stm1)) id : State.t -> State.t</pre>

<p>の s While(condition, stm1) のところに穴をあけて関数化する、つまり、</p>

<pre>fun g -> cond (b condition) (g $ (s stm1)) id : (State.t -> State.t) -> (State.t -> State.t)</pre>

<p>としたときに、「s While(condition, stm1) : State.t -> State.t はこの関数の引数でもあるし戻り値でもある」ということになる。</p>

<p>ということはこの関数について「引数＝戻り値となるような値」を見つけることができればどうだろうか。</p>

<p>fix をそのような手段とすれば While の定義は、</p>

<pre>| While(condition, stm1) ->
    let f g = cond (b condition) (g $ (s stm1)) id in
    fix f</pre>

<p>と書ける。この定義はもはや構成要素だけから決まるようになっている。これが while 文の定義がこんな形になっている理由だった。</p>

<p>なお、ある関数について「引数＝戻り値となるような値」、つまり「f g = g となるような g」のことをその関数の fixed point と呼ぶ。</p>

<p>前回確認したのは関数の fixed point は複数ある場合があり、この定義だと fix が関数ではなく、したがって semantic function 全体も関数にならず、そうすると denotational semantics の目標が達成できないということだった。</p>

<p>だから fix は「fixed point が複数ある場合にもその中から適切な1つを選び取る」ように改修しなければならない。問題はその適切な1つをどのように定義するかだ。</p>

<p>Exercise 5.2 では while ~(x=0) do x := x-1 というプログラムには少なくとも次の2つの関数がその意味になってしまうということを見た。</p>

<pre>let g2 = fun state ->
  if lookup "x" state >= 0
  then update "x" 0 state
  else undef ()
 
let g4 = fun state -> update "x" 0 state</pre>

<p>「適切な1つ」は g2 のほうだ。この2つを見比べると、x が0以上の場合の結果は同じで、0未満だった場合の結果が違うことが見て取れる。0未満のとき g2 は停止せず、g4 は x を 0 にする。「0未満のとき停止せず」っていうのは「0未満の引数に対しては戻り値が定義されていない」ということだ。つまり関数の定義域に関していうと g2 のほうが g4 より小さい。これは g2 を選び取る基準に使えるのではないだろうか？</p>

<p>これ以降 fix を改修していくアイデアのあらすじは次のようなものになる。</p>

<p>- 関数を「順序付け」する方法を導入する<br/>
- fix は「一番小さい fixed point」を選び取るようにする</p>

<p>ここからまた様々な問題が立ちあがってくる。関数を順序付けするってどういうことか？その中で「一番小さいもの」って本当に決められるのか？</p>
]]></content:encoded>
</item>
<item rdf:about="http://rainyday.blog.so-net.ne.jp/2009-05-03">
<title>Semantics with Applications を読んで denotational semantics について知る (1)</title>
<link>http://rainyday.blog.so-net.ne.jp/2009-05-03</link>
<description>これからの一連のエントリは私が &quot;Semantics with Applications&quot; (Nielson &amp;amp; Nielson) の第5章 &quot;Denotational Semantics&quot; を読みながら記録を残していくものです。注意点としては別に翻訳ではないし、理解に間違いを含むかもしれません。この本の第1版はウェブで読めるので怪しいぞと思ったらそちらを参照されるとよいでしょう。http://www.daimi.au.dk/~bra8130/Wiley_book/wiley.html</description>
<dc:subject>未分類</dc:subject>
<dc:creator>ether</dc:creator>
<dc:date>2009-05-03T20:50:07+09:00</dc:date>
<content:encoded><![CDATA[
<p>これからの一連のエントリは私が "Semantics with Applications" (Nielson &amp; Nielson) の第5章 "Denotational Semantics" を読みながら記録を残していくものです。注意点としては別に翻訳ではないし、理解に間違いを含むかもしれません。この本の第1版はウェブで読めるので怪しいぞと思ったらそちらを参照されるとよいでしょう。</p>

<p><a href="http://www.daimi.au.dk/~bra8130/Wiley_book/wiley.html" target="_blank">http://www.daimi.au.dk/~bra8130/Wiley_book/wiley.html</a></p>
<a name="more"></a><h5>Denotational semantics の目標</h5>

<p>Denotational semanticsではプログラミング言語の統語的な構成素それぞれについて、「統語的表現から数学的な対象への関数」を定義する。これをsemantic functionと呼ぶ。統語的な表現というのはベタなプログラムのテキストというよりは構文木をイメージしたい。数学的な対象というのは関数の場合が多い。だからsemantic functionは「構文木の断片を関数に変換する関数」だ。</p>

<img src="http://rainyday.blog.so-net.ne.jp/_images/blog/_c11/rainyday/semanticfunction.png" width="315" height="100" border="0" align="" alt="semanticfunction.png" />

<p>ところで semantics function は厳密な意味で関数である。ということは入力となる構文のすべてに対して結果が定義されてなければいけないし、その結果というのは一意でなければいけない。このような semantics function が定義できればその言語の denotational semantics を定義するのに成功したことになる。</p>

<h5>While 言語</h5>

<p>この本ではWhileという簡易言語を例に使って説明をしている。算術式と真偽式と文の区別があって文には代入とNOPと順次実行と条件分岐と繰り返しがある。変数には数値だけが入って真偽値は入らない。文法は以下の通り。</p>

<pre>a ::= n | x | a1 + a2 | a1 * a2 | a1 - a1
b ::= true | false | a1 = a2 | a1 &lt;= a2 | ~b | b &amp; b2
S ::= x := a | skip | S1; S2 | if b then S1 else S2 | while b do S</pre>

<p>ここで n は数値リテラル（整数のみ）で、x は変数シンボル。これに対する semantic function の定義の仕方をどうするかというと、(1) 算術式、真偽式、文のそれぞれの統語範疇に対して定義し、また、(2) 統語範疇の意味はその構成要素だけから一意に決まるようにする。後者は semantic function が関数だとしたことの帰結でもあるし、フレーゲの構成性原理を満たすための要請でもある。</p>

<h5>算術式の semantic function</h5>

<p>まず a1 + a2 みたいな算術式に対応する semantic function の型は以下のようなものになる。（なお、以降の型の表記方法やその他の数学的表記は適当に OCaml 風に改変しています。そのほうがブログに書きやすいし、それに私の場合、数学的表記には拒否感が働くけどプログラムならイメージしやすいから。でも本当はプログラムではなく数学的関数などを表現するものです）</p>

<pre>val a: aexp -> (State.t -> int)</pre>

<p>a というのが semantic function の名前だ。aexp というのが算術式の構文木の型で、これを semantic function である a に通すことによって State.t -> int という（関数の）型になる。これが算術式の意味だ。</p>

<p>ところで算術式は数値（整数）を表すのだからその semantic function は単純に aexp -> int ではいけないのか。算術式は変数を含むかもしれないのでそうはいかないのだ。変数を含む算術式は環境の影響を受ける。</p>

<p>State.t は現在の環境（変数の状態）を表す型だ。直観を助けるために State.t 型には以下のようなシグニチャがあるのだと思いたい。</p>

<pre>module type State =
  sig
    type t
    val initial_state : t
    val lookup : string -> t -> int
    val update : string -> int -> t -> t
  end</pre>
  
<p>aとaexpはOCaml風に書くと以下のようになるのだろう。</p>

<pre>type aexp = Val of int | Var of string | Add of aexp * aexp ...
let rec a aexp state = match aexp with
| Val(n) -> n
| Var(x) -> lookup x state
| ...</pre>

<h5>真偽式の semantic function</h5>

<p>同様にして真偽式に対する b: bexp -> (State.t -> bool) という semantic function も存在する。変数に入るのは数値だけだけど x &lt;= 1 みたいな式ではやはり環境の影響を受けるので State.t -> bool になる。</p>

<pre>type bexp = T of bool | Eq of bexp * bexp | Neg of bexp | ...
let rec b bexp state = match bexp with
| T(tf) -> tf
| Eq(a1, a2) -> if (a a1 state) = (a a2 state) then true else false
| Neg(b1) -> not (b b1 state)
| ...</pre>

<h5>文の semantic function</h5>

<p>式が State.t -> int や State.t -> bool のような関数になったのに対して、文は State.t -> State.t のような関数になる[1]。前者の State.t が実行前の状態で後者の State.t が実行後の状態というわけだ。文の構文木の型が stm だとして、文に対する semantic function の型はこうなる。</p>

<pre>val s: stm -> (State.t -> State.t)</pre>

<p>この s はどういう内容の関数か。これも OCaml 風に書きあらわすことにする。</p>

<p>まずstmが、</p>

<pre>type stm = Assign of string * aexp | Skip | Seq of stm * stm | Cond of bexp * stm * stm | While of bexp * stm</pre>

<p>という型だということにしてsは以下のように書き表せる。</p>

<pre>(* auxiliary functions *)
let id x = x;;
let ($) f g x = f (g x);;
let cond b s1 s2 = fun state -> if (b state) then (s1 state) else (s2 state);;

let rec s stm = match stm with
| Assign(var, val) -> fun (state : State.t) -> update var (a val state) state
| Skip -> id
| Seq(stm1, stm2) -> (s stm2) $ (s stm1) 
| Cond(condition, stm1, stm2) -> cond (b condition) (stm stm1) (stm stm2)
| While(condition, stm1) ->
    let f g = cond (b condition) (g $ (s stm1)) id in
    fix f</pre>

<p>場合分けの最初の4つは特に難しくないけど最後の While の部分はよくわからない。第一 fix っていう関数をどこでも定義していない。fix については OCaml で考えるのをやめて、「(State.t -> State.t) -> (State.t -> State.t) という型の関数 f を引数に与えられた時に f g = g になるような g を返す関数」と日本語で定義することにする。fix f の戻り値は State.t -> State.t だ。let f g = ... の g は State.t -> State.t で f g の戻り値も State.t -> State.t だ。</p>

<p>ということで fix の型はこうなる。</p>

<pre>val fix: ((State.t -> State.t) -> (State.t -> State.t)) -> (State.t -> State.t)</pre>

<h5>繰り返し文を含むプログラムの例 (Example 5.1)</h5>

<p>本当にこれでうまくいくのだろうか。ということで while ~(x=0) do skip というプログラムを考えて確かめる。構文木は While(&lt;&lt;~(x=0)>>, Skip) のような感じになる。（面倒くさいので Camlp4 風のクォーテーションを導入して &lt;&lt;~(x=0)>> で Neg(Eq(Var("x"),Val(0))) を意味することにします）</p>

<p>そうするとまずは While が出てくるが、f は</p>

<pre>f g = cond (b &lt;&lt;~(x=0)>>) (g $ (s Skip)) id</pre>

<p>となる。</p>

<p>s Skip は id で、g $ id は g と同じだから結局は</p>

<pre>f g = cond (b &lt;&lt;~(x=0)>>) g id</pre>

<p>と書き換えてよい。さらに cond の定義にしたがって、</p>

<pre>f g = fun state -> if (b &lt;&lt;~(x=0)>> state) then (g state) else (id state)</pre>

<p>その後 bexp の中身をいろいろすると結局は、</p>

<pre>f g = fun state -> if not (lookup "x" state = 0) then (g state) else state</pre>

<p>になる。</p>

<p>この f について「f g = g になるようなg」を考えないといけない。以下の g1 はそういう g である。</p>

<pre>let rec undef () = undef ();; 
let g1 = fun state ->
  if not (lookup "x" state = 0)
  then undef ()
  else state</pre>

<p>本当に f g1 = g1 になるかどうか確かめよう。g1 を f に渡して置き換えると、</p>

<pre>fun state ->
  if not (lookup "x" state = 0)
  then
    (fun state -> if not (lookup "x" state = 0) then undef () else state)
    state
  else state</pre>

<p>となるが、これをリファクタリングすると、まず外側の then の中身を簡単にして、</p>

<pre>fun state ->
  if not (lookup "x" state = 0)
  then if not (lookup "x" state = 0) then undef () else state
  else state</pre>

<p>さらにこの then に入るときは x≠0 なのだから結局は内側の if も簡単にできて、</p>

<pre>fun state ->
  if not (lookup "x" state = 0)
  then undef ()
  else state</pre>

<p>これは g1 と同じである。</p>

<p>ところで undef は無限ループする関数なのでこの関数の内容は「x がゼロでないとき無限ループ、ゼロであれば元と同じ状態」ということになる。これは while ~(x=0) do skip というプログラムの意味としてまことに適当である。</p>

<h5>繰り返し文はこの定義では実はうまくいかない</h5>

<p>こんな感じで行けばうまくいきそうな気がするのだけど実際には以下の2点の問題が存在するという。</p>

<p>問題1:「f g = g になるような g」は複数あるかもしれない。そうだとしたら fix 関数は関数のような顔をしながら実は関数ではないということになるし、プログラムの意味が多義的であってもいいということになる。</p>

<p>問題2:「f g = g になるような g」は1つもないかもしれない。もしそうだとしたらそのプログラムには denotational semantics では意味を与えられないことになってしまう。</p>

<p>問題1が問題だというのは我々がプログラミング言語の意味論を考えているからだ。自然言語であれば文の意味は実際に多義的であるかもしれないので、多義的な表現に多義的な意味を付与する意味論は（実際に観察される言語と一致していれば）むしろ好ましいかもしれない。でもプログラミング言語では普通そういうことはない。</p>

<p>問題2は問題1に比べると小さい問題だけど統語解析が終わって型検査も通ったプログラムはとにかく実行できるのだから、それに意味が付与できないことがあるような意味論はやはり頼りないということなのだろう。</p>

<p>というわけで最初に述べた denotational semantics の目標設定に戻ってくる。数学的な関数という意味での semantic function を定義するのがこの意味論の目標なのだ。fix が関数でなければ全体としての semantic function も関数にならない。</p>

<h5>Exercise 5.2 をやろう</h5>

<p>問題1のことを考えながら Exercise 5.2 の while ~(x=0) do x := x-1 というプログラムの意味を確かめてみる。構文木は While(&lt;&lt;~(x=0)>>, &lt;&lt;x := x-1>>) だ。（クォーテーションは式にも文にも使えることにしました）</p>

<p>そうすると f は、</p>

<pre>f g = cond (b &lt;&lt;~(x=0)>>) (g $ (s &lt;&lt;x := x-1>>)) id</pre>

<p>で、condの定義にしたがって、</p>

<pre>f g = fun state ->
  if (b &lt;&lt;~(x=0)>> state)
  then (g $ (s &lt;&lt;x := x-1>>)) state
  else (id state)</pre>

<p>関数合成のところをちょっとわかりやすくして、</p>

<pre>f g = fun state ->
  if (b &lt;&lt;~(x=0)>> state)
  then g (s &lt;&lt;x := x-1>> state)
  else state</pre>

<p>またいろいろして、</p>

<pre>f g = fun state ->
  if not (lookup "x" state = 0)
  then g (update "x" ((lookup "x" state) - 1) state) 
  else state</pre>

<p>となる。</p>

<p>さて、この f の「f g = gになるような g」は次の g1 から g5 のうちのどれか、というのが問いである。</p>

<pre>let g1 = fun state -> undef ()

let g2 = fun state ->
  if lookup "x" state >= 0
  then update "x" 0 state
  else undef ()
 
let g3 = fun state ->
  if lookup "x" state >= 0
  then update "x" 0 state
  else state

let g4 = fun state -> update "x" 0 state

let g5 = fun state -> state</pre>

<p>元の while ~(x=0) do x := x-1 を頭の中で実行してみれば g2 が適切というのは自明だ。でも一応1つ1つ確かめてみよう。</p>

<h5>g1</h5>

<p>まず g1 を f に与える。</p>

<pre>fun state ->
  let g1 = fun state -> undef () in
  if not (lookup "x" state = 0)
  then g1 (update "x" ((lookup "x" state) - 1) state) 
  else state</pre>

<p>g1 は引数に何を取ろうと undef なので、</p>

<pre>fun state ->
  if not (lookup "x" state = 0)
  then undef ()
  else state</pre>

<p>となる。この関数は元の g1 と同じではない。よって g1 は違う。</p>

<h5>g2</h5>

<p>次に g2 を f に与える。</p>

<pre>fun state ->
  let g2 = fun state ->
    if lookup "x" state >= 0
    then update "x" 0 state
    else undef ()
  in
  if not (lookup "x" state = 0)
  then g2 (update "x" ((lookup "x" state) - 1) state) 
  else state</pre>

<p>ここで内側の g2 に与えられる state は外側の state とは違って x をマイナス1したものであることに注意しないといけない。ちょっとこまかく場合分けしよう。</p>

<pre>fun state ->
  let g2 = fun state ->
    if lookup "x" state >= 0
    then update "x" 0 state
    else undef ()
  in
  let state' = update "x" ((lookup "x" state)   1) state in
  match (lookup "x" state) with
  | x when x >= 1 -> g2 state'
  | x when x &lt;= -1 -> g2 state'
  | x when x = 0 -> state</pre>

<p>まず x>=1 のとき、state' の x は x>=0 である。だからこの場合は、</p>

<pre>  | x when x >= 1 -> update "x" 0 state'</pre>

<p>次に x&lt;=-1 のとき、state' の x は x&lt;=-2 である。だから、</p>

<pre>  | x when x &lt;= -1 -> undef ()</pre>
  
<p>となる。state と state' が違うのは x の値だけであるということを考慮してもう1度書き直すと、</p>

<pre>fun state ->
  match (lookup "x" state) with
  | x when x >= 1 -> update "x" 0 state
  | x when x &lt;= -1 -> undef ()
  | x when x = 0 -> state</pre>
  
<p>ここで3つ目のパターンは x が 0 なのだから update "x" 0 state と書き換えても同じである。したがって結局は1つ目と3つ目をまとめてしまって、</p>

<pre>fun state ->
  if lookup "x" state >= 0
  then update "x" 0 state
  else undef ()</pre>

<p>と書き直せる。これは元々の g2 と同じである。</p>

<h5>g3</h5>

<p>次はg3。</p>

<pre>fun state ->
  let g3 = fun state ->
    if lookup "x" state >= 0
    then update "x" 0 state
    else state
  in
  if not (lookup "x" state = 0)
  then g3 (update "x" ((lookup "x" state) - 1) state) 
  else state</pre>

<p>また場合分けして、</p>

<pre>fun state ->
  let g3 = fun state ->
    if lookup "x" state >= 0
    then update "x" 0 state
    else state
  in
  let state' = update "x" ((lookup "x" state) 1) state in
  match (lookup "x" state) with
  | x when x >0 -> g3 state'
  | x when x = 0 -> state
  | x when x &lt; 0 -> g3 state'</pre>

<p>書き直して、</p>

<pre>fun state ->
  let state' = update "x" ((lookup "x" state) 1) state in
  match (lookup "x" state) with
  | x when x >0 -> update "x" 0 state
  | x when x = 0 -> state
  | x when x &lt; 0 -> state'</pre>
  
<p>これは元の g3 と同じではない。</p>

<h5>g4</h5>

<p>次にg4。</p>

<pre>fun state ->
  let g4 = fun state -> update "x" 0 state in
  if not (lookup "x" state = 0)
  then g4 (update "x" ((lookup "x" state) - 1) state) 
  else state</pre>

<p>g4 には x をマイナス1した環境を与えているけど、結局 g4 の中で一律 0 にされるので、</p>

<pre>fun state ->
  if not (lookup "x" state = 0)
  then update "x" 0 state
  else state</pre>

<p>これはつまり x が 0 じゃないときは 0、0 のときはそのままということなので、</p>

<pre>fun state -> update "x" 0 state</pre>

<p>あれ？これは元の g4 と同じだ。</p>

<h5>g5</h5>

<p>最後に g5 はどうか。</p>

<pre>fun state ->
  let g5 = fun state -> state in
  if not (lookup "x" state = 0)
  then g5 (update "x" ((lookup "x" state) - 1) state) 
  else state</pre>

<p>g5 は何もしないので書き直して、</p>

<pre>fun state ->
  if not (lookup "x" state = 0)
  then update "x" ((lookup "x" state) - 1) state
  else state</pre>

<p>おしまい。これは元の g5 とは違う。</p>

<p>ここまでの話を振り返ると、我々の semantic function の定義にしたがうと while ~(x=0) do x := x-1 というプログラムに対しては少なくとも g2 と g4 の2つの関数がその意味として割り当てられてしまうということだ。つまり fix の定義の「f g が g になるような g」という定義では fix が関数になっていない。</p>

<p>というわけで denotational semantics が while ~(x=0) do x := x-1 に適切な意味を付与できるようにするためには fix の定義をもうちょっとうまく改修してあげなければならない。ここからすごく長い道のりが始まるのだ。</p>

<p>[1] 実は止まらないプログラムには「実行後の状態」なんて存在しないから、文に対応するのは部分関数というほうが正確だ。元の本では表記上も区別をしている。ここではしない。</p>


]]></content:encoded>
</item>
<item rdf:about="http://rainyday.blog.so-net.ne.jp/2009-01-04">
<title>Scala から Gainer mini を使う</title>
<link>http://rainyday.blog.so-net.ne.jp/2009-01-04</link>
<description>このところマイコンを使った電子工作に興味を持ち始めました。電子工作をやっているといっても理科の知識は中学生未満だし半田付けもできるだけ避けたいという軟派な感じのものですが。マイコンとしては主にPICという電子工作によく使われている廉価なものを使っていて、アセンブラかCで書いたプログラムをマイコンのフラッシュメモリに書き込むのですが、ちょっとした実験のために使い捨てプログラムが必要になることがあります。しかしそうしたプログラムをいちいちアセンブラやCで書いてライターで書き込むのは面倒くさい。多少の制約（時間的な精度とか）は受け入れても超高級言語が使いたい、と思うことがあります。そこで Gainer mini [http://gainer-mini.jp/] というデバイスを買いました。これはPCとUSBケーブルで接続され、電子回路上の電圧の制御や読み取りがPC上のプログラミング言語から行えるようにパッケージ化されたものです。フィジカルコンピューティングデバイスというカテゴリに含まれるものですが、その言葉の意味はともかくとして、これを使うと電子回路の頭脳の部分を回路上のマイコンから外に出してPC上に移すことが出来ます。プログラミング言語として公式には Flash+ActionScript、Processing、Max/MSP が使え、私はまず Processing を使ってみたのですが、いろいろと言語やライブラリに対する不満が出てきたため、Scala から使うためのライブラリを作ってみました。まずは単純な使用例です。Gainer mini に LED を2つ（dout 0,1）、押しボタンスイッチを1つ（din 0）、ツマミ（可変抵抗）を1つ（ain 0）取り付けます。この回路を使って、「周期的に LED を点滅させる。点滅周期の長さはツマミで制御し、スイッチが押されていないときは dout 0 につながった LED を、押されたときは dout 1 につながった LED を点滅させる。Gainer のオンボードスイッチが押されたら終了」ということをするプログラムを Scala で書きます。val gainer = Gainer(&quot;COM3&quot;)try {  gainer.open()  while (!gainer.button) {    val led = if (gainer.din(0)) 0 else 1 ..</description>
<dc:subject>Scala</dc:subject>
<dc:creator>ether</dc:creator>
<dc:date>2009-01-04T19:45:03+09:00</dc:date>
<content:encoded><![CDATA[
<p>このところマイコンを使った電子工作に興味を持ち始めました。電子工作をやっているといっても理科の知識は中学生未満だし半田付けもできるだけ避けたいという軟派な感じのものですが。</p>

<p>マイコンとしては主にPICという電子工作によく使われている廉価なものを使っていて、アセンブラかCで書いたプログラムをマイコンのフラッシュメモリに書き込むのですが、ちょっとした実験のために使い捨てプログラムが必要になることがあります。しかしそうしたプログラムをいちいちアセンブラやCで書いてライターで書き込むのは面倒くさい。多少の制約（時間的な精度とか）は受け入れても超高級言語が使いたい、と思うことがあります。</p>

<p>そこで Gainer mini [<a href="http://gainer-mini.jp/" target="_blank">http://gainer-mini.jp/</a>] というデバイスを買いました。これはPCとUSBケーブルで接続され、電子回路上の電圧の制御や読み取りがPC上のプログラミング言語から行えるようにパッケージ化されたものです。フィジカルコンピューティングデバイスというカテゴリに含まれるものですが、その言葉の意味はともかくとして、これを使うと電子回路の頭脳の部分を回路上のマイコンから外に出してPC上に移すことが出来ます。</p>

<p>プログラミング言語として公式には Flash+ActionScript、Processing、Max/MSP が使え、私はまず Processing を使ってみたのですが、いろいろと言語やライブラリに対する不満が出てきたため、Scala から使うためのライブラリを作ってみました。</p>

<p>まずは単純な使用例です。Gainer mini に LED を2つ（dout 0,1）、押しボタンスイッチを1つ（din 0）、ツマミ（可変抵抗）を1つ（ain 0）取り付けます。</p>

<div><img src="http://rainyday.blog.so-net.ne.jp/_images/blog/_c11/rainyday/DSCF6106.JPG" width="448" height="336" border="0" align="" alt="DSCF6106.JPG" /><br/>
<img src="http://rainyday.blog.so-net.ne.jp/_images/blog/_c11/rainyday/DSCF6107.JPG" width="448" height="336" border="0" align="" alt="DSCF6107.JPG" /></div>

<p>この回路を使って、「周期的に LED を点滅させる。点滅周期の長さはツマミで制御し、スイッチが押されていないときは dout 0 につながった LED を、押されたときは dout 1 につながった LED を点滅させる。Gainer のオンボードスイッチが押されたら終了」ということをするプログラムを Scala で書きます。</p>

<pre><code>val gainer = Gainer("COM3")

try {
  gainer.open()
  while (!gainer.button) {
    val led = if (gainer.din(0)) 0 else 1
    val interval = gainer.ain(0)
    gainer.dout(led) = true
    Thread.sleep(interval)
    gainer.dout(led) = false
    Thread.sleep(interval)
  }
} finally {
  gainer.close()
}</code></pre>

<p>実行結果です。（動画です）</p>

<div><div class="video-link"><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0" width="300" height="285" id="mini_video_player" align="middle">
<param name="allowScriptAccess" value="sameDomain" />
<param name="movie" value="http://blog.so-net.ne.jp/_mini_video_player/player.swf?dl_link=http%3A%2F%2Fblog.so-net.ne.jp%2F_pages%2Frss_radio%2Fp.pl%3Fd%3D178fb271f4e41cbc0ffe8cf506655a2d%26type%3Ddl&mp3=http%3A%2F%2Fblog.so-net.ne.jp%2F_pages%2Frss_radio%2Fp.pl%3Fd%3D178fb271f4e41cbc0ffe8cf506655a2d%26type%3Dflv&name=DSCF6108.3gp&image_link=%2F_images%2Fblog%2F_c11%2Frainyday%2Fm_DSCF6108-37303.jpg&skinNo=1" />
<param name="menu" value="true" />
<param name="quality" value="best" />
<param name="bgcolor" value="#ffffff" />
<param name="wmode" value="transparent" />
<embed src="http://blog.so-net.ne.jp/_mini_video_player/player.swf?dl_link=http%3A%2F%2Fblog.so-net.ne.jp%2F_pages%2Frss_radio%2Fp.pl%3Fd%3D178fb271f4e41cbc0ffe8cf506655a2d%26type%3Ddl&mp3=http%3A%2F%2Fblog.so-net.ne.jp%2F_pages%2Frss_radio%2Fp.pl%3Fd%3D178fb271f4e41cbc0ffe8cf506655a2d%26type%3Dflv&name=DSCF6108.3gp&image_link=%2F_images%2Fblog%2F_c11%2Frainyday%2Fm_DSCF6108-37303.jpg&skinNo=1" menu="true" quality="best" bgcolor="#ffffff" width="300" height="285" wmode="transparent" name="mini_video_player" align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" />
</object>
</div></div>

<p>以下がライブラリのソースコードです。Gainer mini のライブラリが使っているのと同じ RXTX というシリアル通信用の Java ライブラリを使っています。RXTXcomm.jar を Scala の lib ディレクトリに、rxtxSerial.dll（Windows 前提で話していますが）をパスの通った場所においてください。</p><a name="more"></a><pre><code>import gnu.io._
import java.io._
import scala.actors._
import scala.actors.Actor._
import scala.util.DynamicVariable
import scala.collection.mutable.{ArrayBuffer,Queue}

object Gainer {
  implicit def enum2Iter[T](enum: java.util.Enumeration[T]): Iterator[T] = {
    new Iterator[T] {
      def hasNext:Boolean =  enum.hasMoreElements()
      def next:T = enum.nextElement()
    }
  }

  def getCommPortIdentifier(name: String) = {
    import CommPortIdentifier._
    getPortIdentifiers.asInstanceOf[java.util.Enumeration[CommPortIdentifier]].
      filter{_.getPortType == PORT_SERIAL}.
      filter{_.getName == name}.
      toList.firstOption
  }

  implicit val callback = actor { loop { react { case _ => () }}} 
 
  def apply(portName: String)
    = new Gainer(portName, Mode.Mode1, callback)
  def apply(portName: String, mode: Mode.Value)(implicit callback: Actor)
    = new Gainer(portName, mode, callback)
}

object Mode extends Enumeration {
  val Mode1 = Value("1")
  val Mode2 = Value("2")
  val Mode3 = Value("3")
  val Mode4 = Value("4")
  val Mode5 = Value("5")
  val Mode6 = Value("6")
//  val Mode7 = Value("7")
//  val Mode8 = Value("8")
}

abstract class Result
case class Button(pressed: Boolean) extends Result
case class Din(port: Int, high: Boolean) extends Result
case class Ain(port: Int, value: Int) extends Result

abstract class Command
case class DinReq(replyTo: Actor) extends Command
case class DinRes(values: Array[Boolean]) extends Command
case class AinReq(replyTo: Actor) extends Command
case class AinRes(values: Array[Int]) extends Command
case class Async(command: String) extends Command

class Gainer(portName: String, mode: Mode.Value, callback: Actor)
    extends SerialPortEventListener {
  import Gainer._
  override def toString = portName

  private val Some(portId) = getCommPortIdentifier(portName)

  case class Port(port: SerialPort, input: InputStream, output: OutputStream)

  private var port: Option[Port] = None

  def open() = {
    if (port.isEmpty) {
      val port = portId.open("Scala-Gainer", 10000).asInstanceOf[SerialPort]
      try {
        val input = port.getInputStream()
        val output = port.getOutputStream()
        this.port = Some(Port(port, input, output))

        port.setSerialPortParams(
          38400, 8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);

        agent ! Async("KONFIGURATION_" + mode + "*")
        Thread.sleep(100)

        port.addEventListener(this)
        port.notifyOnDataAvailable(true)
      } catch {
        case _ => close()
      }
    }
    this
  }

  def close() {
    port.foreach{ case Port(port, input, output) =>
      input.close()
      output.close()
      port.removeEventListener()
      port.close()
    }
    port = None
  }

  private val agent = actor {
    def send(command: String) {
      port.foreach{ case Port(_, _, output) =>
        output.write(command.getBytes)
        output.flush()
      }
    }
    val dinReplyTo = new Queue[Actor]()
    val ainReplyTo = new Queue[Actor]()
    loop {
      react {
        case DinReq(replyTo) => dinReplyTo += replyTo; send("R*")
        case r @ DinRes(_)   => dinReplyTo.dequeue ! r
        case AinReq(replyTo) => ainReplyTo += replyTo; send("I*")
        case r @ AinRes(_)   => ainReplyTo.dequeue ! r
        case Async(command) => send(command)
      }
    } 
  }

  // on-board LED
  private var ledStatus = false
  def led = ledStatus
  def led_= (led: Boolean) = agent ! Async(if (led) "h*" else "l*")

  // on-board button
  private var buttonStatus = false
  def button = buttonStatus

  // digital output pins
  object dout {
    private[Gainer] val values = new Array[Boolean](16)
    def apply(i: Int) = values(i)
    def update(i: Int, v: Boolean) {
      if (i &lt; 0 || i > 0xf) error("port number out of range: " + i)
      agent ! Async((if (v) "H" else "L") + i.toHexString.toUpperCase + "*")
    }
  }

  // digital input pins
  object din {
    val values = new Array[Boolean](16)
    def apply(i: Int) = { peek(); values(i) }
    def peek() = {
      agent ! DinReq(self)
      receiveWithin(1000) {
        case DinRes(values) => values.copyToArray(this.values, 0)
      }
    }
    def begin() { agent ! Async("r*") }
    def end() { agent ! Async("E*") }
  }

  // analog output pins
  object aout {
    private[Gainer] val values = new Array[Int](16)
    def apply(i: Int) = values(i)
    def update(i: Int, v: Int) {
      if (i &lt; 0 || i > 0xf) error("port number out of range: " + i)
      if (v &lt; 0 || v > 0xff) error("analog value out of range: " + i)
      agent ! Async("a%1x%02x*".format(i, v))
    }
  } 

  // analog input pins
  object ain {
    val values = new Array[Int](16)
    def apply(i: Int) = { peek(); values(i) }
    def peek() = {
      agent ! AinReq(self)
      receiveWithin(1000) {
        case AinRes(values) => values.copyToArray(this.values, 0)
      }
    }
    def begin() { agent ! Async("I*") }
    def end() { agent ! Async("E*") }
  }

  // serial port event handler

  private val buffer = new ArrayBuffer[Byte]()

  def serialEvent(ev: SerialPortEvent) = synchronized {
    ev.getEventType match {
      case SerialPortEvent.DATA_AVAILABLE =>
        port.foreach{ case Port(_, input, _) =>
          while (input.available > 0) {
            input.read() match {
              case '*' => propagate(new String(buffer.toArray)); buffer.clear()
              case c => buffer += c.asInstanceOf[Byte]
            }
          }
        }
    }
  }

  private var prevR = ""
  private var prevI = ""

  private def propagate(r: String) {
    r.substring(0,1) match {
      case "N" => buttonStatus = true;  callback ! Button(true)
      case "F" => buttonStatus = false; callback ! Button(false)
      case "h" => ledStatus = true
      case "l" => ledStatus = false
      case "H" => dout.values(Integer.parseInt(r.substring(1, 2), 16)) = true
      case "L" => dout.values(Integer.parseInt(r.substring(1, 2), 16)) = false
      case "R" => val bitmap = Integer.parseInt(r.substring(1, 5), 16)
                  val ar = for (i &lt;- 0 to 15) yield (bitmap &amp; (1 &lt;&lt; i)) > 0
                  agent ! DinRes(ar.toArray)
      case "r" => if (r != prevR) {
                    prevR = r
                    val bitmap = Integer.parseInt(r.substring(1, 5), 16)
                    for (i &lt;- 0 to 15) {
                      val newValue = (bitmap &amp; (1 &lt;&lt; i)) > 0
                      if (din.values(i) != newValue) {
                        din.values(i) = newValue
                        callback ! Din(i, newValue)
                      }
                    }
                  }
      case "a" => val port = Integer.parseInt(r.substring(1, 2), 16)
                  val value = Integer.parseInt(r.substring(2, 4), 16)
                  aout.values(port) = value
      case "I" => val ar = for (i &lt;- 1 to r.length - 3 by 2)
                             yield Integer.parseInt(r.substring(i, i + 2), 16)
                  agent ! AinRes(ar.toArray)
      case "i" => if (r != prevI) {
                    prevI = r
                    val ar = for (i &lt;- 1 to r.length - 3 by 2) 
                               yield Integer.parseInt(r.substring(i, i + 2), 16)
                    for (i &lt;- 0 to ar.length) {
                      if (ain.values(i) != ar(i)) {
                        ain.values(i) = ar(i)
                        callback ! Ain(i, ar(i))
                      }
                    }
                  }
      case _ => ()
    }
  }
}</code></pre>

<p>最後に簡単に使い方を説明します。</p>

<p>* 生成</p>

<p>Gainer mini を使うにはまず Gainer オブジェクトを生成します。直接 new Gainer(...) するか、Gainer シングルトンオブジェクトの apply を呼ぶかします。引数は最大3つで、COMポート名、モード、コールバックを受けるアクターです。</p>

<p>例:</p>
<pre><code>val gainer = Gainer("COM3")</code></pre>

<p>* 接続・切断</p>

<p>Gainer と接続するには Gainer#open、切断するには Gainer#close を呼びます。</p>

<p>* デジタル出力</p>

<p>デジタル出力ピンを変化させるには Gainer#dout を配列風操作で設定します。読み取りも配列風に出来ますが、反映は非同期で、Gainer から応答が返ってきたときに更新されます。</p>

<p>例:</p>
<pre><code>gainer.dout(0) = true // dout 0 を High にする</code></pre>

<p>* デジタル入力</p>

<p>デジタル入力ピンを読み取るには Gainer#din を配列風操作で読み取ります。この際 Gainer に問い合わせを行い、同期的に戻り値を返します（ここが Gainer の Processing ライブラリなどとは違います）。</p>

<p>例:</p>
<pre><code>val value: Boolean = gainer.din(0) // din 0 の状態を読み取る</code></pre>

<p>この値は単純に電圧が High のとき true、Low のとき false になります。プルアップ接続のときには負論理になります。</p>

<p>Gainer#din#peek は Gainer に全デジタル入力ピンの状態の問い合わせを1度行い、結果を Gainer#din#values 配列に入れます。これも同期的です。Gainer#din#values には書き込みも出来ますが、しても何も起こりません。</p>

<p>Gainer#din#begin を呼ぶとデジタル入力ピンの状態が持続的に Gainer#din#values に更新され続けるようになります。またコールバック用のアクターを指定している場合にはアクターに Din(port, high) というメッセージ（port はピン番号、high は状態）が送信されます（変化があったときのみ）。この間中、Gainer と PC の間を（状態の変化があろうがなかろうが）データが流れ続けています（Gainer から出てくる時点で更新時のみになってくれたほうがいいのですが…）。やめるには Gainer#din#end を呼びます。</p>

<p>* アナログ出力・アナログ入力</p>

<p>API はデジタルの場合と同様で、aout, ain となります。値は 0 から 255 までの Int として表現されます。Gainer#ain#begin を使った場合は Ain(port, value) メッセージがコールバックアクターに送られます。</p>

<p>* オンボードLED</p>

<p>Gainer mini 上の LED を点灯・消灯させたいときは Gainer#led プロパティを使います。設定時の反映は非同期です。</p>

<p>例:</p>
<pre><code>gainer.led = true // LED点灯</code></pre>

<p>* オンボードスイッチ</p>

<p>Gainer mini 基板上のボタンの状態は Gainer#button に更新されます。またコールバックアクターに Button(pressed) メッセージが送信されます。</p>

<p>ところでこのライブラリを使うと、終了時に JVM がいつまでも終わってくれないということがままあります。多分シリアル通信ライブラリがらみだと思うのですがよくわかりません。</p>

]]></content:encoded>
</item>
<item rdf:about="http://rainyday.blog.so-net.ne.jp/2008-12-13">
<title>Programming in Scala が届いた</title>
<link>http://rainyday.blog.so-net.ne.jp/2008-12-13</link>
<description>記念写真です。結局当初の予定からどれだけ遅れたことになるんだろう。読む時間を作らないと…</description>
<dc:subject>Scala</dc:subject>
<dc:creator>ether</dc:creator>
<dc:date>2008-12-13T13:51:03+09:00</dc:date>
<content:encoded><![CDATA[
<p>記念写真です。結局当初の予定からどれだけ遅れたことになるんだろう。</p>

<p><img src="http://rainyday.blog.so-net.ne.jp/_images/blog/_c11/rainyday/DSCF5986.JPG" width="314" height="235" border="0" align="" alt="DSCF5986.JPG" /></p>

<p>読む時間を作らないと…</p><a name="more"></a>
]]></content:encoded>
</item>
<item rdf:about="http://rainyday.blog.so-net.ne.jp/2008-10-25">
<title>チェック例外の特徴を整理する</title>
<link>http://rainyday.blog.so-net.ne.jp/2008-10-25</link>
<description>Java を勉強していてエキゾチックだと感じるのはやはりチェック例外だ。このチェック例外という仕組みは、便利か邪魔か、ということはひとまず置くとしてやはり非常に興味深いものだと思う。そんな Java のチェック例外を自分なりに理解しやすいように整理・言い換えをしてみた。（これも前回と同様にかなり雑な内容だと思いますがとりあえずあきらめて記事にしてしまいます）まずメソッドの throws 節に書かれる例外クラスを戻り値の型と比べてみよう。戻り値の型は実際に return される型と不整合があるとコンパイルエラーになる。同様に throws 節の例外クラスと実際にメソッドから投げられうる例外との間に不整合があるとコンパイルエラーになる。いずれもメソッドのクライアントに対する契約の一部をなしているから、宣言された型より小さい型を実際に返す/投げる分には構わないが逆は不可である。例えば Object m() throws Exception {} は String を返したり IOException を投げたりできるが、String m() throws IOException は Object を返したり Exception を投げたりできない。（なお、この記事では継承関係にあってサブクラスに向かうほど小さい、逆を大きいと呼ぶ。≦と≧に対応する不等号として &amp;lt;: と &amp;gt;: を使う）また、メソッドがサブクラスでオーバーライドされるとき、オーバーライドするメソッドの throws 節にはオーバーライドされたメソッドの throws 節と同じか、それよりも小さい型を指定しなければならない。例えば throws Exception を throws IOException でオーバーライドできるが、逆は不可である。これは Java1.5 からは戻り値についても同じである (covariant overriding)。逆に言うと例外には昔から covariant overriding があったともいえる。戻り値とチェック例外の類似点は一応ここまでである。チェック例外は戻り値とは異なり互いに継承関係にない複数の型を指定することができる。throws IOException, ClassNotFoundExceptionという宣言は IOException か ClassNotFoundException を投げるかもし..</description>
<dc:subject>Java</dc:subject>
<dc:creator>ether</dc:creator>
<dc:date>2008-10-25T21:45:34+09:00</dc:date>
<content:encoded><![CDATA[
<p>Java を勉強していてエキゾチックだと感じるのはやはりチェック例外だ。このチェック例外という仕組みは、便利か邪魔か、ということはひとまず置くとしてやはり非常に興味深いものだと思う。そんな Java のチェック例外を自分なりに理解しやすいように整理・言い換えをしてみた。（これも前回と同様にかなり雑な内容だと思いますがとりあえずあきらめて記事にしてしまいます）</p>

<p>まずメソッドの throws 節に書かれる例外クラスを戻り値の型と比べてみよう。戻り値の型は実際に return される型と不整合があるとコンパイルエラーになる。同様に throws 節の例外クラスと実際にメソッドから投げられうる例外との間に不整合があるとコンパイルエラーになる。</p>

<p>いずれもメソッドのクライアントに対する契約の一部をなしているから、宣言された型より小さい型を実際に返す/投げる分には構わないが逆は不可である。例えば Object m() throws Exception {} は String を返したり IOException を投げたりできるが、String m() throws IOException は Object を返したり Exception を投げたりできない。（なお、この記事では継承関係にあってサブクラスに向かうほど小さい、逆を大きいと呼ぶ。≦と≧に対応する不等号として &lt;: と >: を使う）</p>

また、メソッドがサブクラスでオーバーライドされるとき、オーバーライドするメソッドの throws 節にはオーバーライドされたメソッドの throws 節と同じか、それよりも小さい型を指定しなければならない。例えば throws Exception を throws IOException でオーバーライドできるが、逆は不可である。これは Java1.5 からは戻り値についても同じである (covariant overriding)。逆に言うと例外には昔から covariant overriding があったともいえる。<p>

<p>戻り値とチェック例外の類似点は一応ここまでである。チェック例外は戻り値とは異なり互いに継承関係にない複数の型を指定することができる。</p>

<pre><code>throws IOException, ClassNotFoundException</code></pre>

<p>という宣言は IOException か ClassNotFoundException を投げるかもしれないが、例えば（同様に Exception の直接のサブクラスである）InterruptedException は投げないという約束になる。一方で戻り値の型は1つだけだ。</p>

<p>ちなみに「互いに継承関係にない」と断り書きをつけたのはもし継承関係にあればその大きいほうのクラスに包摂できるからで throws Exception, IOException というのは throws Exception というのと同じことである。</p>

<p>「一応ここまで」と書いたのは、次のように捉えなおすことで例外と戻り値の型とのアナロジーを引き続き維持しようという企みからである。</p>

<p>戻り値の型とは別に、そして例外クラス（とその階層）とも別物として、メソッドの「チェック例外に関する型」（およびその階層）を考える。これを仮に「throws の型」と呼ぶことにする。「throws の型」は単に継承関係にない例外クラスの集合として表現される型である。{IOException, ClassNotFoundException} のように表記することにする。</p>

<p>throws の型は次のような規則で階層をなす。</p>

<p>(1) 2つの throws の型が包含関係にあるとき、部分集合のほうの型のほうが小さい。したがって throws IOException, ClassNotFoundException を throws IOException でオーバーライドできる。</p>

<p>(2) また、throws の型の要素の一部を例外クラス階層においてより小さいものに置き換えるとより小さい型になる。したがって throws IOException, ClassNotFoundException を throws FileNotFoundException, ClassNotFoundException でオーバーライドできる。</p>

<p>例外クラスの階層と throws の型の階層の間の関係をいくつか例をあげて示す。</p>

<p>チェック例外のクラスが A, B, C の3つしかなく、以下の継承関係にあるとき、</p>

<div><img src="http://rainyday.blog.so-net.ne.jp/_images/blog/_c11/rainyday/tree1.gif" width="144" height="125" border="0" align="" alt="tree1.gif" /></div>

<p>throws の型の階層は以下のようになる。</p>

<div><img src="http://rainyday.blog.so-net.ne.jp/_images/blog/_c11/rainyday/poset1-60455.gif" width="124" height="240" border="0" align="" alt="poset1.gif" /></div>

</p>例外の階層が以下だったら、

<div><img src="http://rainyday.blog.so-net.ne.jp/_images/blog/_c11/rainyday/tree2-45427.gif" width="63" height="192" border="0" align="" alt="tree2.gif" /></div>

<p>こうなる。</p>

<div><img src="http://rainyday.blog.so-net.ne.jp/_images/blog/_c11/rainyday/poset2-0292b.gif" width="57" height="240" border="0" align="" alt="poset2.gif" /></div>

<p>例外のクラスが A, B, C, D の4つで以下の継承関係にあるとき、</p>

<div><img src="http://rainyday.blog.so-net.ne.jp/_images/blog/_c11/rainyday/tree3-60091.gif" width="137" height="192" border="0" align="" alt="tree3.gif" /></div>

<p>throws の型の階層は以下のようになる。(*)</p>

<div><img src="http://rainyday.blog.so-net.ne.jp/_images/blog/_c11/rainyday/poset3-810b5.gif" width="130" height="288" border="0" align="" alt="poset3.gif" /></div>

<p>例外の階層が以下だったら、</p>

<div><img src="http://rainyday.blog.so-net.ne.jp/_images/blog/_c11/rainyday/tree4-f4bc0.gif" width="192" height="108" border="0" align="" alt="tree4.gif" /></div>

<p>こうなる。</p>

<div><img src="http://rainyday.blog.so-net.ne.jp/_images/blog/_c11/rainyday/poset4-834ee.gif" width="220" height="288" border="0" align="" alt="poset4.gif" /></div>

<p>これらは全部プログラムで出力しているのできちんと変換方法を定義できるが、大雑把にいうとまず例外クラスをメンバとする全ての集合の包含関係で階層を作り、それを例外クラスの階層に基づいて正規化すると throws の型階層ができる。（TODO: throws の型階層が必ず lattice であることを示したい）</p>

<p>あらゆる throws の型に対して下にくる (bottom) のが空集合からなる型である。つまりどんなメソッドでも throws を書かないメソッドでオーバーライドできる（勿論 final でなければ）。あらゆる throws の型に対して上に来る型 (top) は例外クラスのルート一つだけからなる集合として構成できる（上の例ではすべて {A}）。これは例外クラスがツリー階層をなす（top を持つ）ということからくる効用である。</p>

<p>このように throws の型を定義するとオーバーライドについて先ほどと同じ言い方を維持できる。オーバーライドするメソッドの throws 節にはオーバーライドされたメソッドの throws 節と同じか、それよりも小さい型を指定しなければならない。</p>

<p>話はここで終わりではない。インターフェイスが関与する場合は複数のメソッドをオーバーライドするということがありうるので、それら全ての throws の型に対して下に来るような型でなければならない。さっきの (*) を付けた図でいうと throws C と throws B, D をオーバーライドするメソッドは throws D か例外を投げないメソッドでなければならない。</p>

<p>throws の型の階層では2つの要素から下に辿ったときに最初に出会う地点が1つだけ決まる (meet) ので「この型と同じか、それより小さい型」という風に単一の型を使って表現できる。今の例だと「{D} か、それより小さい型」だ。</p>

<p>次に、throws 節に指定する型はメソッド本体の定義からも制約を受ける。メソッド本体はその実装から「こういう例外を投げうる」ということを求めることができる。Java の仕様書では "possible result" とか "can throw ..." （...は例外クラス）とかいう言葉で表現されているが、やや見通しの悪い書き方になっているので throws の型の観点から再構成してみよう。</p>

<p>メソッドがどんな例外を投げうるかを定式化することを考える。関数型言語などでは式の型は（日本語で書くと）以下のような規則（ML 風文法を想定）を定義して型を推論したり検査したりする。</p>

<p>・式 a が型αで式 b が 型βのとき、式 a; b の型はβ<br/>
・式 f が型α→βで式 a が型αのとき、式 (f a) の型はβ<br/>
・式 c が型 bool で式 t と式 f がともに型αのとき、式 if c then t else f の型はα</p>

<p>このやり方を throws の型に適用すると以下のようになるだろう。なお、以下でα∨βは「αとβから上に辿っていって最初に出会うところ」(join) を意味するものとする（throws の型については「和集合をとって正規化」にだいたい対応）。</p>

<p>・文 a が型αで文 b が型βのとき、ブロック {a; b} の型はα∨β<br/>
・式 a が型αでメソッド b が型βで式 C が型γのとき、式 a.b(c) の型はα∨β∨γ<br/>
・式 a が型αでブロック b が型βでブロック C が型γのとき、文 if (a) {b} else {c} の型はα∨β∨γ<br/>
などなど。</p>

<p>以上から throws の型の特徴として次のようなことが観察できる。先ほどの ML 風文法のための規則は式の組み合わせ方について型の制約を追加で課していた。これに対して throws の型は統語的な組み合わせにはまったく影響を与えない。</p>

<p>たったいま述べた観察に対する唯一の例外は try/catch である。（finally と catch 節複数の場合は省略）</p>

<p>・ブロック a が型αでブロック b が型βのとき、文 try {a} catch (E id) {b} の型は γ∨β。ただしγは{E}>:αのとき{}、それ以外のときγ∨{E}={E}∨αとなるような最小の型で、γ=αとなるときエラー</p>

<p>「ただし」以降は結構考えた末にこうなったけどまだ考慮漏れがあるかもしれない。「γ=αとなるときエラー」が「投げられるはずがない例外をキャッチしてはいけない」に対応している。[2008-10-28追記]もうちょっと良く考えてみると{E}>:αのとき「γ∨{E}={E}∨αとなるような最小のγ」は「γ∨{E}={E}となる最小のγ」で {} だ。だから場合わけは不要だった。こうなってみるとこの定義は Java の仕様書よりもずっといい感じだ。</p>

<p>こうした規則に基づいてメソッド本体の throws の型（「こういう例外を投げうる」）が求まる。メソッドの throws 節にはこうして求まった型と同じかそれより大きい型を書かなければならない。</p>

<p>というわけで「throws の型」という概念を導入したことによりメソッドの throws 節に書くことが許される型 T について、</p>

<p>「T は U 以下で L 以上のものである（ただし U はオーバーライドされる型全ての meet で、L はメソッド本体の型）」</p>

<p>という言い方で述べることができるようになった。</p>

<p>さて、throws 節に書いていい型の範囲はわかった。ところで実際にメソッドのオーバーライドをして、そのメソッド本体の throws の型がオーバーライドされるメソッドの型より小さいとき、どちらを選択する（まあ中間という選択肢はないだろうから）のが慣習として好ましいのだろう。</p>

<p>例えば void m() throws Exception というメソッドをオーバーライドして、メソッド本体は実際には FileNotFoundException しか投げえない場合、オーバーライドする側のメソッドには throws FileNotFoundException と書くべきだろうか、それとも throws Exception と書くべきだろうか。</p>

<p>サブクラスを利用するクライアントに対してより informative なのは前者のほうである。しかしこのクラスをさらに継承する側の実装者に対してより高い自由度を与えるのは後者である。こういうケースには定説があるのだろうか（例えば Effective Java みたいな本に書いてあるとか）。</p><a name="more"></a>
]]></content:encoded>
</item>
<item rdf:about="http://rainyday.blog.so-net.ne.jp/2008-10-03">
<title>covariant なコレクションと構造的型付けの話</title>
<link>http://rainyday.blog.so-net.ne.jp/2008-10-03</link>
<description>前回の記事で言及した「structural な型付けしか存在しないオブジェクト指向言語」についてまだ少し考えていました。この記事はあまり裏をとっていない話が混じっているので注意して読んでください（誤りだと思ったら指摘してほしいです）。最初に Scala の話を一つ。Scala で immutable で covariant なコレクション、例えば List を使うと、空の状態では要素の型は Nothing になっていて、異なる型の要素を追加するにつれて Any に近づいていく。scala&amp;gt; class Adefined class Ascala&amp;gt; class B extends Adefined class Bscala&amp;gt; class C extends Bdefined class Cscala&amp;gt; class D extends Bdefined class Dscala&amp;gt; class E extends Ddefined class Escala&amp;gt; class F extends Edefined class Fscala&amp;gt; val list1 = List()list1: List[Nothing] = List()scala&amp;gt; val list2 = new F :: list1list2: List[F] = List(F@158aeed)scala&amp;gt; val list3 = new E :: list2list3: List[E] = List(E@e964fe, F@158aeed)scala&amp;gt; val list4 = new C :: list3list4: List[B] = List(C@9403a3, E@e964fe, F@158aeed)scala&amp;gt; val list5 = 123 :: list4list5: List[Any] = List(123, C@9403a3, E@e964fe, F@158aeed)ここで List[E] に C を追加すると List[B] と型推論される。この B は E と C という2つのクラスから上に辿っていって初めて出会う地点 (join) だ。クラス階層はツリーだからそういう地点は必ず1つ存在し、1つしかない。ここで E と C の共通の祖先 (upper bounds) とし..</description>
<dc:subject>未分類</dc:subject>
<dc:creator>ether</dc:creator>
<dc:date>2008-10-03T00:22:42+09:00</dc:date>
<content:encoded><![CDATA[
<p>前回の記事で言及した「structural な型付けしか存在しないオブジェクト指向言語」についてまだ少し考えていました。この記事はあまり裏をとっていない話が混じっているので注意して読んでください（誤りだと思ったら指摘してほしいです）。</p>

<p>最初に Scala の話を一つ。</p>

<p>Scala で immutable で covariant なコレクション、例えば List を使うと、空の状態では要素の型は Nothing になっていて、異なる型の要素を追加するにつれて Any に近づいていく。</p>

<pre><samp>scala> class A
defined class A

scala> class B extends A
defined class B

scala> class C extends B
defined class C

scala> class D extends B
defined class D

scala> class E extends D
defined class E

scala> class F extends E
defined class F

scala> val list1 = List()
list1: List[Nothing] = List()

scala> val list2 = new F :: list1
list2: List[F] = List(F@158aeed)

scala> val list3 = new E :: list2
list3: List[E] = List(E@e964fe, F@158aeed)

scala> val list4 = new C :: list3
list4: List[B] = List(C@9403a3, E@e964fe, F@158aeed)

scala> val list5 = 123 :: list4
list5: List[Any] = List(123, C@9403a3, E@e964fe, F@158aeed)</samp></pre>

<div><img src="http://rainyday.blog.so-net.ne.jp/_images/blog/_c11/rainyday/image001.gif" width="102" height="310" border="0" align="" alt="image001.gif" /></div>

<p>ここで List[E] に C を追加すると List[B] と型推論される。この B は E と C という2つのクラスから上に辿っていって初めて出会う地点 (join) だ。クラス階層はツリーだからそういう地点は必ず1つ存在し、1つしかない。ここで E と C の共通の祖先 (upper bounds) としては B の他にも A や A から Any までの間のクラスがあるのだが、いきなり上にいくと不便だからそのうちの一番下のものが選ばれる (least upper bound)。</p>

<p>trait を含む型の階層はツリーではないから話は少し複雑になる。</p>

<pre><samp>scala> trait T
defined trait T

scala> trait U
defined trait U

scala> trait V
defined trait V

scala> class X extends B with T with U with V
defined class X

scala> trait W
defined trait W

scala> class Y extends D with T with U with W
defined class Y

scala> val list6 = new X :: list1
list6: List[X] = List(X@13e86ec)

scala> val list7 = new Y :: list6
list7: List[B with T with U] = List(Y@30380, X@13e86ec)</samp></pre>

<p>trait を含む型の階層構造において、型は継承関係によって順序づけられるが、任意の2つの型を取り上げたとき、それらは継承関係にあるかもしれないしないかもしれないという、それ以上の性質は期待できない (partially ordered set) 。</p>

<div><img src="http://rainyday.blog.so-net.ne.jp/_images/blog/_c11/rainyday/image003.gif" width="223" height="262" border="0" align="" alt="image003.gif" /></div>

<p>この例では X と Y の共通の祖先には A, B, T, U （および A から Any までのクラス）があるが、結果の要素型として推論される型は祖先の集合の「底」に来る要素すべて (minimal elements) を足したものになる。すなわち B, T, U になる。
クラスだけを考えたときは「任意の2つの型を取り上げて上に辿ると、どこか1地点で出会う」という性質を期待できた (join semilattice) から、この中にクラスは1つしか存在しない。そのクラスを with の左に持ってきて残りの trait を右に持ってくれば B with T with U となる。</p>

<p>ちなみに何も継承していない trait を関与させると共通の祖先のない例が作れそうに思えるが、Scala の型推論ではそういう場合は実は ScalaObject with SomeTrait と見なされるようだ（エラーにはならない）。</p>

<pre><samp>scala> List[U]() ::: List[T]()
res6: List[ScalaObject] = List()</samp></pre>

<p>と、ここまでが前置き。空想上の「structural な型付けしか存在しないオブジェクト指向言語」ではクラスは単にメソッドのシグニチャの集合であって、名前があるとしたら単にエイリアスでしかないものとする。この言語のクラス階層では「任意の2つの型を取り上げて上に辿ると、どこか1地点で出会う」が成立し、さらに逆方向（下向き）でも成立する (lattice)。これは集合の包含関係がそうであるというのと全く同型である。</p>

<div><img src="http://rainyday.blog.so-net.ne.jp/_images/blog/_c11/rainyday/image004.gif" width="168" height="165" border="0" align="" alt="image004.gif" /></div>

<p>この言語にも Scala のように covariant な型付けをするコレクションがあると考える。メソッドとして a と b と c しか定義されていないとしよう。そうすると例えば {a, b, c} list という型のリストに {a, b} 型の要素を追加したら {a, b} list に、{a, b} list に {b, c} を追加したら {b} list になるはずだ。{a, b} list に {c} を追加したら {} list になるのだろう。</p>

<p>さて空のリストの型は何から始めたらいいのだろうか。上に上っていくわけだから、最初はクラス階層の一番下 (bottom element) でなければならない。クラスはメソッドのシグニチャの集合でしかないという定義から、メソッドとして a, b, c があれば {a, b, c} list で a, b, c, d だったら {a, b, c, d} list ということになる。だけどプログラム内すべてを見渡して定義されたメソッドの情報がすべて分からないと空リストの型が決まらないというのはどうもおかしい。</p>

<p>というわけで「プログラムでクラスがどう定義されようがすべてのクラスに対して一番下に来るクラス」を導入する必要性が出てくる。Scala の Nothing 型も存在するすべてのクラスを持ち上げる (lifting) ようなクラスだった。</p>

<div><img src="http://rainyday.blog.so-net.ne.jp/_images/blog/_c11/rainyday/image005.gif" width="168" height="202" border="0" align="" alt="image005.gif" /></div>

<p>ところでこのクラスは「メソッドのシグニチャの集合」には還元できない。だからこの型だけは名前によって直接名指されなければならない。</p>

<p>さて structural な型付けのオブジェクト指向といえば OCaml だけど、この辺どうなっているのだろうか。</p>

<pre><samp># let list1 = [];;
val list1 : 'a list = []
# let list2 = object method a () = () method b () = () end :: list1;;
val list2 : &lt; a : unit -> unit; b : unit -> unit > list = [&lt;obj>]
# let list3 = object method a () = () method c () = () end :: list2;;
This expression has type &lt; a : unit -> unit; b : unit -> unit > list
but is here used with type &lt; a : unit -> unit; c : unit -> unit > list
Only the first object type has a method b</samp></pre>

<p>あれ－。</p>

<p>じゃあリスト自作で、と思ってクラス定義について調べると Scala 同様に variance の指定はできるようなんだけど Nothing 相当についてはどうしたらいいのか？</p>

2008-10-13追記:図を作成して挿入した<a name="more"></a>
]]></content:encoded>
</item>
<item rdf:about="http://rainyday.blog.so-net.ne.jp/2008-09-27">
<title>Java における直列化と型</title>
<link>http://rainyday.blog.so-net.ne.jp/2008-09-27</link>
<description>このところめっきりプログラミングをしていないのだけど、最近は SJC-P の教科書を買って Java を勉強していて、直列化周りの言語仕様に興味と疑問が湧いた。Java ではクラスが Serializable インターフェイスを implements すると ObjectOutputStream とかを使ってシリアライズできるようになる。この Serializable インターフェイスは実装すべきメソッドというものがない空のインターフェイスであって、これはマーカーインターフェイスと呼ばれる。まずこのマーカーインターフェイスというのがちょっと興味深い。これは単にこの名前のインターフェイスを implements しているということだけに意味があるものだ。例えば nominal な型付けというものが全くなくて structural な型付けしか存在しないような静的なオブジェクト指向言語を考えてみると、そういう言語ではマーカーインターフェイスの居場所が無い。だからマーカーインターフェイスの用途すべてが他の仕組みに還元できるわけではないということが示せれば nominal な型付けを完全放棄してはいけない根拠の一つになるかもしれない。次に疑問点として、何故 ObjectOutputStream#writeObject の引数は Serializable obj ではなくて Object obj なのだろうか、ということを思った。writeObject は Serializable でないオブジェクトを受け取ると実行時エラーになる、つまりマーカーインターフェイスのマーカーは実行時に利用されているわけだけど、引数を Serializable にすると現在手に入るコンパイラの仕組みでこれをコンパイルエラーにすることができる。もっというと Serializable を implements したクラスのすべてのフィールドが直列化可能な型であるということも、やろうと思えばコンパイル時チェックできるのではないだろうか。後半はともかく前半に対する反論を考えてみると、たまたま Object 型（や Serializable を implements していないクラス）の変数に入っているオブジェクトが実際には直列化可能だとわかっている場合（サブクラスで Serializable を implements していて、そのクラスのインスタンス..</description>
<dc:subject>Java</dc:subject>
<dc:creator>ether</dc:creator>
<dc:date>2008-09-27T14:45:35+09:00</dc:date>
<content:encoded><![CDATA[
<p>このところめっきりプログラミングをしていないのだけど、最近は SJC-P の教科書を買って Java を勉強していて、直列化周りの言語仕様に興味と疑問が湧いた。</p>

<p>Java ではクラスが Serializable インターフェイスを implements すると ObjectOutputStream とかを使ってシリアライズできるようになる。この Serializable インターフェイスは実装すべきメソッドというものがない空のインターフェイスであって、これはマーカーインターフェイスと呼ばれる。</p>

<p>まずこのマーカーインターフェイスというのがちょっと興味深い。これは単にこの名前のインターフェイスを implements しているということだけに意味があるものだ。例えば nominal な型付けというものが全くなくて structural な型付けしか存在しないような静的なオブジェクト指向言語を考えてみると、そういう言語ではマーカーインターフェイスの居場所が無い。だからマーカーインターフェイスの用途すべてが他の仕組みに還元できるわけではないということが示せれば nominal な型付けを完全放棄してはいけない根拠の一つになるかもしれない。</p>

<p>次に疑問点として、何故 ObjectOutputStream#writeObject の引数は Serializable obj ではなくて Object obj なのだろうか、ということを思った。</p>

<p>writeObject は Serializable でないオブジェクトを受け取ると実行時エラーになる、つまりマーカーインターフェイスのマーカーは実行時に利用されているわけだけど、引数を Serializable にすると現在手に入るコンパイラの仕組みでこれをコンパイルエラーにすることができる。もっというと Serializable を implements したクラスのすべてのフィールドが直列化可能な型であるということも、やろうと思えばコンパイル時チェックできるのではないだろうか。</p>

<p>後半はともかく前半に対する反論を考えてみると、たまたま Object 型（や Serializable を implements していないクラス）の変数に入っているオブジェクトが実際には直列化可能だとわかっている場合（サブクラスで Serializable を implements していて、そのクラスのインスタンスである）を救えないことになってしまう。</p>

<p>…と思ったのだけど、そう状況ではしょうがないのでキャストすればいいのではないだろうか。一般に Serializable にキャストできないのかとも思ったけどそうでもないようだ（変換元が final でなければ可能）。その場合 NotSerializableException が ClassCastException になるだけの違いで、「キャストを使わない限りはコンパイル時に検出可能」という線までは前進できる。</p>

<p>それともそこまで前進しても現実的な使用では意味がないのだろうか。ひょっとしたら何か根本的な思い違いをしているような気もするけど釈然としない。</p><a name="more"></a>
]]></content:encoded>
</item>
<item rdf:about="http://rainyday.blog.so-net.ne.jp/2008-07-09">
<title>Java の本当の発明について</title>
<link>http://rainyday.blog.so-net.ne.jp/2008-07-09</link>
<description>Java が登場してその良さが何かにつけて喧伝されていた頃によく「彼らがあたかも Java の発明であるかのように言っていることは C++ と比べた場合の Java の特徴ではあるかもしれないが、その起源は Java ではなく、ずっと昔からあるものだ」というような反論も合わせて耳にした。それはいいとして、本当に Java の発明になるものがないかといえば実はあって、「チェック例外」というものがそうだ、と思ったのだけどこれは間違ってるでしょうか。もうひとつ、Java 以降に現れた言語で Java のチェック例外を真似した言語はない、という気もするのだけど、これは本当でしょうか。（C# には？）</description>
<dc:subject>Java</dc:subject>
<dc:creator>ether</dc:creator>
<dc:date>2008-07-09T00:13:06+09:00</dc:date>
<content:encoded><![CDATA[
Java が登場してその良さが何かにつけて喧伝されていた頃によく「彼らがあたかも Java の発明であるかのように言っていることは C++ と比べた場合の Java の特徴ではあるかもしれないが、その起源は Java ではなく、ずっと昔からあるものだ」というような反論も合わせて耳にした。<br />
<br />
それはいいとして、本当に Java の発明になるものがないかといえば実はあって、「チェック例外」というものがそうだ、と思ったのだけどこれは間違ってるでしょうか。<br />
<br />
もうひとつ、Java 以降に現れた言語で Java のチェック例外を真似した言語はない、という気もするのだけど、これは本当でしょうか。（C# には？）<a name="more"></a>
]]></content:encoded>
</item>
<item rdf:about="http://rainyday.blog.so-net.ne.jp/2008-06-16">
<title>Prolog で型チェック</title>
<link>http://rainyday.blog.so-net.ne.jp/2008-06-16</link>
<description>Prolog の力といえば単一化ですが、[1] の記事でやったような型チェックの場合も、同じ型であるかどうかの判断というのは単一化でうまく書けそうです。型チェックのところだけ書きたいので構文木は適当に与えられているものとします。整数と真偽値と条件分岐と変数束縛とラムダから成る簡単な言語を想定して Prolog で型チェックを行ってみます。type_of(Num, _, number) :- number(Num).type_of(true, _, bool).type_of(false, _, bool).type_of(if(Cond, True, False), Env, T) :-  type_of(Cond, Env, bool),  type_of(True, Env, T),  type_of(False, Env, T).type_of(let(Sym, Val, Body), Env, T) :-  type_of(Val, Env, T2),  extend_env(Env, Sym, T2, Env2),  type_of(Body, Env2, T).type_of(Var, Env, T) :- atom(Var), apply_env(Env, Var, T).type_of(lambda(Arg, Body), Env, (T1 -&amp;gt; T2)) :-  extend_env(Env, Arg, T1, Env2),  type_of(Body, Env2, T2).extend_env(Env, Sym, Val, [Sym:Val | Env]).apply_env([Sym:Val | _], Sym, Val) :- !.apply_env([_:_ | Rest], Sym, Val) :- apply_env(Rest, Sym, Val).ここで a -&amp;gt; b とか a:b は単なる中置演算子で書いた構造で特別なものではありません（それぞれ -&amp;gt;(a,b), :(a,b) と同じ）。単一化のおかげで Scheme のときのより簡潔に書けているというだけでも大分ありがたいのですが、さらにボーナスがついています。ちょっとプログラムをテストしてみましょう。?- type_of(123, [], X).X = numberYes?- type_of(true, [..</description>
<dc:subject>Prolog</dc:subject>
<dc:creator>ether</dc:creator>
<dc:date>2008-06-16T22:42:23+09:00</dc:date>
<content:encoded><![CDATA[
<p>Prolog の力といえば単一化ですが、[1] の記事でやったような型チェックの場合も、同じ型であるかどうかの判断というのは単一化でうまく書けそうです。</p>

<p>型チェックのところだけ書きたいので構文木は適当に与えられているものとします。
整数と真偽値と条件分岐と変数束縛とラムダから成る簡単な言語を想定して Prolog で型チェックを行ってみます。</p>


<pre><code>type_of(Num, _, number) :- number(Num).
type_of(true, _, bool).
type_of(false, _, bool).

type_of(if(Cond, True, False), Env, T) :-
  type_of(Cond, Env, bool),
  type_of(True, Env, T),
  type_of(False, Env, T).

type_of(let(Sym, Val, Body), Env, T) :-
  type_of(Val, Env, T2),
  extend_env(Env, Sym, T2, Env2),
  type_of(Body, Env2, T).

type_of(Var, Env, T) :- atom(Var), apply_env(Env, Var, T).

type_of(lambda(Arg, Body), Env, (T1 -> T2)) :-
  extend_env(Env, Arg, T1, Env2),
  type_of(Body, Env2, T2).

extend_env(Env, Sym, Val, [Sym:Val | Env]).

apply_env([Sym:Val | _], Sym, Val) :- !.
apply_env([_:_ | Rest], Sym, Val) :- apply_env(Rest, Sym, Val).</code></pre>

<p>ここで a -> b とか a:b は単なる中置演算子で書いた構造で特別なものではありません（それぞれ ->(a,b), :(a,b) と同じ）。</p>
<p>単一化のおかげで Scheme のときのより簡潔に書けているというだけでも大分ありがたいのですが、さらにボーナスがついています。</p>
<p>ちょっとプログラムをテストしてみましょう。</p>

<pre><samp>?- type_of(123, [], X).

X = number

Yes
?- type_of(true, [], X).

X = bool

Yes
?- type_of(if(1,2,3), [], X).

No
?- type_of(if(true,2,3), [], X).

X = number

Yes
?- type_of(let(x,1,x), [], X).

X = number

Yes
?- type_of(let(x,false,x), [], X).

X = bool

Yes
?- type_of(lambda(x,if(x,1,2)), [], X).

X = bool->number

Yes
?- type_of(lambda(x,if(true,x,2)), [], X).

X = number->number

Yes
?- type_of(lambda(x,x), [], X).

X = _G215->_G215

Yes</samp></pre>

<p>特筆すべきは最後のラムダのところです。</p>
<p>まず [1] の記事の実装では関数の引数の型は明示的に書かなければならなかったのですが、ここでは特別な追加実装なしに引数の型推論もしてくれています。type_of(lambda... のところで Arg の型 T1 はインスタンス化されないまま型環境に追加されて後で単一化により型が決まっているわけです。</p>
<p>また最後の例のように何でもいい型は最後まで型がインスタンス化されないということによって表現されます。</p>
<p>Prolog の機能におんぶにだっこになることによってこうしたことが「ただで」手に入ったということですね。</p>

<p>追記:関数適用の文法を含めるのを忘れてた。まあいいか。</p>

<p>[1] <a href="http://rainyday.blog.so-net.ne.jp/2008-03-30" target="_blank">http://rainyday.blog.so-net.ne.jp/2008-03-30</a></p>
<a name="more"></a>
]]></content:encoded>
</item>
<item rdf:about="http://rainyday.blog.so-net.ne.jp/2008-06-03">
<title>Prolog の Definite Clause Grammar を試す (2)</title>
<link>http://rainyday.blog.so-net.ne.jp/2008-06-03</link>
<description>以前 [1] の記事で DCG で Indexed Grammar を書くことを思いついていろいろ失敗していた。その後、2冊目の Prolog の本として Gazdar &amp; Mellish の &quot;Natural Language Processing in Prolog&quot; を買ったのだが、それを読むと DCG を取り上げた部分で Indexed Grammar についての話も出てきて、しかもちゃんと無限ループに陥らない書き方の例がいくつか書いてあった。{ a^n b^n c^n : n &amp;gt;= 0 } はこう書くといい。（Gazdar &amp; Mellish の例とは index の表現方法を変えている）s              --&amp;gt; s([]).s(Indices)     --&amp;gt; b(Indices), c(Indices).s(Indices)     --&amp;gt; [a], s([i|Indices]).b([i|Indices]) --&amp;gt; [b], b(Indices).b([])          --&amp;gt; [].c([i|Indices]) --&amp;gt; [c], c(Indices).c([])          --&amp;gt; [].これだと以下の最後の例のように非文の判定もできる。?- s([], []).Yes?- s([a,b,c], []).Yes?- s([a,a,b,b,c,c], []).Yes?- s(X, []).X = [] ;X = [a, b, c] ;X = [a, a, b, b, c, c] ;X = [a, a, a, b, b, b, c, c, c]Yes?- s([a,a,b,c,c], []).No[1] の記事の文法では t のルールで index を生産して a,b,c のルールで消費する形だったのが、a が生産側で b,c が消費側という非対称な形になっている。{ xx : x ∈ {a,b}* } はこう書ける。（前掲の本だと { xx : x ∈ {a,b,c}* } だったけど。これも index の表現を変えている）s              --&amp;gt; x([]).x(Indices)     --&amp;gt; y(Indices).x(Indices)     --&amp;gt; [a], x([a|Indices])..</description>
<dc:subject>Prolog</dc:subject>
<dc:creator>ether</dc:creator>
<dc:date>2008-06-03T23:13:57+09:00</dc:date>
<content:encoded><![CDATA[
<p>以前 [1] の記事で DCG で Indexed Grammar を書くことを思いついていろいろ失敗していた。</p>

<p>その後、2冊目の Prolog の本として Gazdar & Mellish の "Natural Language Processing in Prolog" を買ったのだが、それを読むと DCG を取り上げた部分で Indexed Grammar についての話も出てきて、しかもちゃんと無限ループに陥らない書き方の例がいくつか書いてあった。</p>

<p>{ a^n b^n c^n : n >= 0 } はこう書くといい。（Gazdar & Mellish の例とは index の表現方法を変えている）</p>

<pre><code>s              --> s([]).
s(Indices)     --> b(Indices), c(Indices).
s(Indices)     --> [a], s([i|Indices]).
b([i|Indices]) --> [b], b(Indices).
b([])          --> [].
c([i|Indices]) --> [c], c(Indices).
c([])          --> [].</code></pre>

<p>これだと以下の最後の例のように非文の判定もできる。</p>

<pre><samp>?- s([], []).

Yes
?- s([a,b,c], []).

Yes
?- s([a,a,b,b,c,c], []).

Yes
?- s(X, []).

X = [] ;

X = [a, b, c] ;

X = [a, a, b, b, c, c] ;

X = [a, a, a, b, b, b, c, c, c]

Yes
?- s([a,a,b,c,c], []).

No</samp></pre>

<p>[1] の記事の文法では t のルールで index を生産して a,b,c のルールで消費する形だったのが、a が生産側で b,c が消費側という非対称な形になっている。</p>

<p>{ xx : x ∈ {a,b}* } はこう書ける。（前掲の本だと { xx : x ∈ {a,b,c}* } だったけど。これも index の表現を変えている）</p>

<pre><code>s              --> x([]).
x(Indices)     --> y(Indices).
x(Indices)     --> [a], x([a|Indices]).
x(Indices)     --> [b], x([b|Indices]).
y([a|Indices]) --> y(Indices), [a].
y([b|Indices]) --> y(Indices), [b].
y([])          --> [].</code></pre>

<p>上記2つはいずれも Indexed Grammar の形をしてるんだけど [1] の記事とは違って非文を与えたときにちゃんと停止する。t のルールが問題の元凶だったようだ。</p>

<p>{ a^n b^n^2 : n >= 1 } については載っていなかったので自分で考えてみる。</p>

<pre><code>s              --> [a], s([i]).
s(Indices)     --> b(Indices).
s(Indices)     --> [a], s([i|Indices]).
b([])          --> [].
b([i|Indices]) --> [b], b(Indices), z(Indices), z(Indices).
z([])          --> [].
z([i|Indices]) --> [b], z(Indices).</code></pre>

<pre><samp>?- s(X,[]).

X = [a, b] ;

X = [a, a, b, b, b, b] ;

X = [a, a, a, b, b, b, b, b, b|...] [write]

X = [a, a, a, b, b, b, b, b, b, b, b, b] ;

X = [a, a, a, a, b, b, b, b, b, b, b, b, b, b, b, b, b, b, b, b]

Yes
?- s([a,b],[]).

Yes
?- s([a,a,b,b,b,b],[]).

Yes
?- s([a,a,b,b,b],[]).

No</samp></pre>

<p>コツがつかめてきた。ついでに本の練習問題だった a^m b^n c^m d^n を書く。</p>

<pre><code>s              --> x([]).
x(Indices)     --> y(Indices).
x(Indices)     --> [b], x([b|Indices]).
x(Indices)     --> [a], x([a|Indices]).
y([a|Indices]) --> y(Indices), [c].
y([b|Indices]) --> y(Indices), [d].
y([])          --> [].</code></pre>

<pre><samp>?- s([a,b,c,d],[]).

Yes
?- s([a,a,b,c,c,d],[]).

Yes
?- s([a,b,b,c,d,d],[]).

Yes
?- s([a,a,b,b,c,d,d],[]).

No
?- s([a,b,b,c,d],[]).

No</samp></pre>

<p>追記: 最後のはまた間違えてた...　これだと s([a,b,a,c,d,c],[]). も Yes になってしまう。書き直したのが以下。</p>

<pre><code>s              --> x([]).
x(Indices)     --> y(Indices).
x(Indices)     --> [a], x([a|Indices]).
y(Indices)     --> z(Indices).
y(Indices)     --> [b], y([b|Indices]).
z([a|Indices]) --> z(Indices), [c].
z([b|Indices]) --> z(Indices), [d].
z([])          --> [].</code></pre>

<p>[1] <a href="http://rainyday.blog.so-net.ne.jp/2008-05-12" target="_blank">http://rainyday.blog.so-net.ne.jp/2008-05-12</a></p><a name="more"></a>
]]></content:encoded>
</item>
<item rdf:about="http://rainyday.blog.so-net.ne.jp/2008-05-13">
<title>Prolog による LFG</title>
<link>http://rainyday.blog.so-net.ne.jp/2008-05-13</link>
<description>LFG (Lexical Functional Grammar) とは何なのかというのは以前手短に書いた [1] のでそちらを参照されたい。その記事を書いたときは LFG の素性構造を Ruby のハッシュとして表現していて、その素性構造を単一化するのに結構苦心して、バグを出したりしていた。あと破壊的な操作を導入したのでいまいちコードの正しさに確信が持てていない。Prolog を使ったらそれが解決されるかというと別にそうでもなくて、やっぱり素性構造のような順序を問わない構造の単一化は難しい。リストであれば例えば [X,y,Z] と [x,Y,z] を単一化したときに [x,y,z] という実体が「両方向から見える」ようになるのだけど {:a =&amp;gt; &quot;a&quot;, :b =&amp;gt; &quot;b&quot;、:d =&amp;gt; &quot;d&quot;} と {:d =&amp;gt; &quot;d&quot;, :c =&amp;gt; &quot;c&quot;, :b =&amp;gt; &quot;b&quot;} を単一化したときに両方から {:a =&amp;gt;&quot;a&quot;, :b =&amp;gt; &quot;b&quot;, :c =&amp;gt; &quot;c&quot;, :d =&amp;gt; &quot;d&quot;} と見えるようにしないといけないというのはやっぱり破壊的に書かずにいられないのではなかろうか。（ちなみに、ひょっとしたら LiLFeS [2] っていう言語がそういうことができるものなのかもしれないのだが、これは以前試そうとしたら手元の環境でコンパイル不能だったので深追いしなかった）それでこの問題の一番簡単な回避方法は、順序を問わない構造というのを直接表現するのをやめて普通のリストを使い、素性は名前でなくて位置から決まるようにすればいい。ここでは5要素のリストが [Subj, Pred, Num, Tense, Pers] という素性の値に対応するとみなすことにする。この決め事に基づいて極めて簡略化された英文法を Prolog の DCG で表現すると以下のようになる。/* f-structure: [Subj, Pred, Num, Tense, Pers] */s([Subj|X]) --&amp;gt; np(Subj), vp([Subj|X]).np(F) --&amp;gt; n(F).vp(F) --&amp;gt; v(F).n([_,lion,pl,_,_]) --&amp;gt; [lions].v([[_,_,pl,_,3],sleep,_,pres,_]) --&amp;gt; [sleep]..</description>
<dc:subject>Prolog</dc:subject>
<dc:creator>ether</dc:creator>
<dc:date>2008-05-13T23:44:08+09:00</dc:date>
<content:encoded><![CDATA[
<p>LFG (Lexical Functional Grammar) とは何なのかというのは以前手短に書いた [1] のでそちらを参照されたい。その記事を書いたときは LFG の素性構造を Ruby のハッシュとして表現していて、その素性構造を単一化するのに結構苦心して、バグを出したりしていた。あと破壊的な操作を導入したのでいまいちコードの正しさに確信が持てていない。</p>

<p>Prolog を使ったらそれが解決されるかというと別にそうでもなくて、やっぱり素性構造のような順序を問わない構造の単一化は難しい。</p>

<p>リストであれば例えば [X,y,Z] と [x,Y,z] を単一化したときに [x,y,z] という実体が「両方向から見える」ようになるのだけど {:a => "a", :b => "b"、:d => "d"} と {:d => "d", :c => "c", :b => "b"} を単一化したときに両方から {:a =>"a", :b => "b", :c => "c", :d => "d"} と見えるようにしないといけないというのはやっぱり破壊的に書かずにいられないのではなかろうか。（ちなみに、ひょっとしたら LiLFeS [2] っていう言語がそういうことができるものなのかもしれないのだが、これは以前試そうとしたら手元の環境でコンパイル不能だったので深追いしなかった）</p>

<p>それでこの問題の一番簡単な回避方法は、順序を問わない構造というのを直接表現するのをやめて普通のリストを使い、素性は名前でなくて位置から決まるようにすればいい。ここでは5要素のリストが [Subj, Pred, Num, Tense, Pers] という素性の値に対応するとみなすことにする。</p>

<p>この決め事に基づいて極めて簡略化された英文法を Prolog の DCG で表現すると以下のようになる。</p>

<pre><code>/* f-structure: [Subj, Pred, Num, Tense, Pers] */

s([Subj|X]) --> np(Subj), vp([Subj|X]).

np(F) --> n(F).
vp(F) --> v(F).

n([_,lion,pl,_,_]) --> [lions].

v([[_,_,pl,_,3],sleep,_,pres,_]) --> [sleep].
v([[_,_,sg,_,3],sleep,_,pres,_]) --> [sleeps].</code></pre>

<p>単一化を言語がサポートしているというのは本当に強力だと思う。これを以下のように使うと、文の f-structure を求めることができる。</p>

<pre><samp>?- s(F,[lions,sleep],[]).

F = [[_G224, lion, pl, _G233, 3], sleep, _G245, pres, _G251] ;

No</samp></pre>

<p>インスタンス化されない変数が残ってしまうのはこの実装における素性構造の扱いを先のように決めたからでしょうがない。以下のように非文を与えるときちんと失敗する。これは sg と pl が単一化に失敗するからだ。</p>

<pre><samp>?- s(F,[lions,sleeps],[]).

No</samp></pre>

<p>さらに f-structure から文を求めることもできる。これは前回 Ruby で実装したものではできなかったようなことだ。</p>

<pre><samp>?- s([[_,lion,pl,_,3],sleep,_,pres,_],S,[]).

S = [lion, sleeps] ;

No</samp></pre>

<p>ところでついでに文のツリー構造（LFG でいう c-structure）も見れるようにした。</p>

<pre><code>s(s(NP,VP),[Subj|X]) --> np(NP,Subj), vp(VP,[Subj|X]).

np(np(N),F) --> n(N,F).
vp(vp(V),F) --> v(V,F).

n(n(lions),[_,lion,pl,_,_]) --> [lions].

v(v(sleep),[[_,_,pl,_,3],sleep,_,pres,_]) --> [sleep].
v(v(sleeps),[[_,_,sg,_,3],sleep,_,pres,_]) --> [sleeps].</code></pre>

<pre><samp>?- s(C,F,S,[]).

C = s(np(n(lions)), vp(v(sleep)))
F = [[_G251, lion, pl, _G260, 3], sleep, _G279, pres, _G285]
S = [lions, sleep] ;

No</samp></pre>

<p>[1] <a href="http://rainyday.blog.so-net.ne.jp/2007-04-09" target="_blank">http://rainyday.blog.so-net.ne.jp/2007-04-09</a><br/>
[2] <a href="http://www-tsujii.is.s.u-tokyo.ac.jp/lilfes/index-j.html" target="_blank">http://www-tsujii.is.s.u-tokyo.ac.jp/lilfes/index-j.html</a></p><a name="more"></a>
]]></content:encoded>
</item>
<item rdf:about="http://rainyday.blog.so-net.ne.jp/2008-05-12">
<title>Prolog の Definite Clause Grammar を試す</title>
<link>http://rainyday.blog.so-net.ne.jp/2008-05-12</link>
<description>DCG (Definite Clause Grammar) はProlog に組み込みのパーサ記法のようなもので、以下のような感じで文法を表現すると構文解析ができるようになる。sentence --&amp;gt; noun_phrase, verb_phrase.noun_phrase --&amp;gt; determiner, noun.verb_phrase --&amp;gt; verb.verb_phrase --&amp;gt; verb, noun_phrase.determiner --&amp;gt; [the].noun --&amp;gt; [man].noun --&amp;gt; [apple].verb --&amp;gt; [eats].verb --&amp;gt; [sings].基本的には上記の通り文脈自由文法なんだけど非終端記号がパラメタを持つことができて、その単一化を利用して例えば自然言語の性や数の一致を文法への制約として付け加えたりもできる。ところで非終端記号に付加情報がついたものといわれて Indexed Grammar を連想した。たまたま本で数ページ紹介されているのを読んだ程度の知識しか無いのだが、Indexed Grammar というのは基本的には文脈自由文法なのだけど、非終端記号に index と呼ばれるシンボルの列をくっつけることができて、文法規則はこのシンボル列を左辺から右辺の非終端記号に引き継いだり、追加したり削除したりできる。ただし追加と削除はスタック的な操作しか許されない。こういう仕組みによって Indexed Grammar は文脈自由文法と文脈依存文法の中間の生成力を持つ。らしい。文脈自由文法で表現できないが Indexed Grammar で表現できる言語としては以下のものがある。{ a^n b^n c^n : n &amp;gt;= 0 } （同じ数の a,b,c のこの順に並べた文を生成する）{ xx : x ∈ {a,b}* } （a と b からなる文字列を2回繰り返した文を生成）{ a^n b^n^2 : n &amp;gt;= 1 } （a を n 回、b を n^2 回繰り返した文を生成）最初の言語を Indexed Grammar 風の DCG で表現すると以下のようになる。s              --&amp;gt; t([]).t(Indices)     --&amp;gt; a(Indices), b(Indices..</description>
<dc:subject>Prolog</dc:subject>
<dc:creator>ether</dc:creator>
<dc:date>2008-05-12T23:11:06+09:00</dc:date>
<content:encoded><![CDATA[
<p>DCG (Definite Clause Grammar) はProlog に組み込みのパーサ記法のようなもので、以下のような感じで文法を表現すると構文解析ができるようになる。</p>

<pre><code>sentence --> noun_phrase, verb_phrase.
noun_phrase --> determiner, noun.
verb_phrase --> verb.
verb_phrase --> verb, noun_phrase.
determiner --> [the].
noun --> [man].
noun --> [apple].
verb --> [eats].
verb --> [sings].</code></pre>

<p>基本的には上記の通り文脈自由文法なんだけど非終端記号がパラメタを持つことができて、その単一化を利用して例えば自然言語の性や数の一致を文法への制約として付け加えたりもできる。</p>

<p>ところで非終端記号に付加情報がついたものといわれて Indexed Grammar を連想した。たまたま本で数ページ紹介されているのを読んだ程度の知識しか無いのだが、Indexed Grammar というのは基本的には文脈自由文法なのだけど、非終端記号に index と呼ばれるシンボルの列をくっつけることができて、文法規則はこのシンボル列を左辺から右辺の非終端記号に引き継いだり、追加したり削除したりできる。ただし追加と削除はスタック的な操作しか許されない。こういう仕組みによって Indexed Grammar は文脈自由文法と文脈依存文法の中間の生成力を持つ。らしい。</p>

<p>文脈自由文法で表現できないが Indexed Grammar で表現できる言語としては以下のものがある。</p>

<p>{ a^n b^n c^n : n >= 0 } （同じ数の a,b,c のこの順に並べた文を生成する）<br/>
{ xx : x ∈ {a,b}* } （a と b からなる文字列を2回繰り返した文を生成）<br/>
{ a^n b^n^2 : n >= 1 } （a を n 回、b を n^2 回繰り返した文を生成）</p>

<p>最初の言語を Indexed Grammar 風の DCG で表現すると以下のようになる。</p>

<pre><code>s              --> t([]).
t(Indices)     --> a(Indices), b(Indices), c(Indices).
t(Indices)     --> t([i|Indices]).
a([])          --> [].
b([])          --> [].
c([])          --> [].
a([i|Indices]) --> [a], a(Indices).
b([i|Indices]) --> [b], b(Indices).
c([i|Indices]) --> [c], c(Indices).</code></pre>

<p>つまり t(Indices) --> t([i|Indices]). のプロダクションが i という index を任意の数まで増やす役目をしていて、t(Indices) --> a(Indices), b(Indices), c(Indices). でその数を a,b,c に引き継ぐ。a,b,c の規則はそのインデクスを1つずつ消費していく。これを試してみると、</p>

<pre><samp>?- s([], []).

Yes
?- s([a,b,c], []).

Yes
?- s([a,a,b,b,c,c], []).

Yes
?- s(X, []).

X = [] ;

X = [a, b, c] ;

X = [a, a, b, b, c, c] ;

X = [a, a, a, b, b, b, c, c, c]

Yes</samp></pre>

<p>という感じでここまでは嬉しいくらいうまくいく。でも言語に属さない文を与えると止まらなくなってしまった。</p>

<pre><samp>?- s([a,a,b,c,c], []).
ERROR: Out of global stack
?- s([a,a,c,c], []).
ERROR: Out of global stack</samp></pre>

<p>先の2つ目の言語は Indexed Grammar だと以下のように定義できる。</p>

<pre><code>s                  --> t([]).
t(Indices)         --> front(Indices), rear(Indices).
t(Indices)         --> t([a|Indices]).
t(Indices)         --> t([b|Indices]).
front([])          --> [].
front([a|Indices]) --> [a], front(Indices).
front([b|Indices]) --> [b], front(Indices).
rear([])           --> [].
rear([a|Indices])  --> [a], rear(Indices).
rear([b|Indices])  --> [b], rear(Indices).</code></pre>

<p>ここでは t の下2つの規則がインデクスとして任意の a,b の列を作りだす。ひとしきり作り終わったところで front と rear の規則に引き継いでそれぞれ作り出されたとおりの文字列を生成する。これは DCG としてうまくいくだろうか。</p>

<pre><samp>?- s(X,[]).

X = [] ;

X = [a, a] ;

X = [a, a, a, a] ;

X = [a, a, a, a, a, a] ;

X = [a, a, a, a, a, a, a, a]

Yes
?- s([],[]).

Yes
?- s([a,a], []).

Yes
?- s([a,a,a,a], []).

Yes
?- s([a,b], []).
ERROR: Out of local stack
   Exception: (27,925) t([a, a, a, a, a, a, a, a|...], [a, b], []) ? abort
% Execution Aborted
?- s([a,a,b,b], []).
ERROR: Out of local stack
   Exception: (27,868) t([a, a, a, a, a, a, a, a|...], [a, a, b, b], []) ? abort
% Execution Aborted</samp></pre>

<p>インスタンス化されていない変数を与えた場合は front と rear が同じつまらない文しか生成してくれなくなった。さらに言語に含まれるかどうかの判定では言語に含まれているものでも front と rear が違う文だと止まらなくなってしまった。（2008-06-03追記: 訂正！front と rear が違う文は言語に含まれません。ひどい勘違い。[a,b,a,b] みたいなのをつまらなくない文法的な文の例として出すべきところでした。「言語に含まれているものでも～」という部分は正しくないです。）</p>

<p>3つめの言語は以下のように書ける。</p>

<pre><code>s              --> t([]).
t(Indices)     --> a(Indices), b(Indices).
t(Indices)     --> t([i|Indices]).
a([])          --> [a].
a([i|Indices]) --> [a], a(Indices).
b([])          --> [b].
b([i|Indices]) --> [b], b(Indices), z(Indices), z(Indices).
z([])          --> [b].
z([i|Indices]) --> [b], z(Indices).</code></pre>

<p>実験結果。これは1つ目と同じような感じだ。</p>

<pre><samp>?- s(X,[]).

X = [a, b] ;

X = [a, a, b, b, b, b] ;

X = [a, a, a, b, b, b, b, b, b|...] [write]

X = [a, a, a, b, b, b, b, b, b, b, b, b] ;

X = [a, a, a, a, b, b, b, b, b, b, b, b, b, b, b, b, b, b, b, b]

Yes
?- s([a,b],[]).

Yes
?- s([a,a,b,b,b,b],[]).

Yes
?- s([a,a,b,b,b],[]).
ERROR: Out of global stack</samp></pre>

<p>それで以上のような結果をどう整理したらいいのかというと、実はよく理解できていない。多分再帰下降型のパーサが文脈自由文法のある制限された形しか扱えないのと同様に、DCG でも文法が表現できるということと、それがそのまま Prolog のトップダウン/バックトラック付きのパーシングで解析できるかどうかというのは別の話だということだろうか。</p>

<p>2008-05-15追記:なんか Indexed Grammar の例を考え出したせいで複雑になったけど、単純に Prolog 一般と同様に DCG でも左再帰がだめってことの気がしてきた。</p><a name="more"></a>
]]></content:encoded>
</item>
<item rdf:about="http://rainyday.blog.so-net.ne.jp/2008-05-11-1">
<title>Prolog でスタック言語を作る</title>
<link>http://rainyday.blog.so-net.ne.jp/2008-05-11-1</link>
<description>Prolog で簡単なスタック言語を作ってみた。組み込み関数は以下の通り。- dup: スタックの先頭を複製してスタックに積む- swap: スタックの最初の2つを入れ替える- pop: スタックの最初の要素を取り除く- add: スタックの最初の2つを取り除いてそれらを合計したものを積む- if: スタックの先頭の真偽値に応じて2つ目ないし3つ目の関数を実行- cons: スタックの最初の要素を2つ目の要素（リスト）にconsする- emptyp: スタックの最初の要素を取り除き、それが空リストだったらtrue、それ以外だったらfalseをスタックに置く- car: スタックの先頭要素（リスト）を取り除き、そのcarを積む- cdr: スタックの先頭要素（リスト）を取り除き、そのcdrを積む- app: スタックの先頭要素（関数）を適用する- papp: スタックの先頭要素（関数）を2つ目の要素にだけ部分適用するこれが実行例。?- exec([], [12,dup], X).X = [12, 12] ;No?- exec([], [12,34,swap], X).X = [12, 34] ;No?- exec([], [12,34,pop], X).X = [12] ;No?- exec([], [12,34,add], X).X = [46] ;No?- exec([], [12,true,[dup,add],[34,swap],if], X).X = [24] ;No?- exec([], [12,false,[dup,add],[34,swap],if], X).X = [12, 34] ;No?- exec([], [nil,12,cons,34,cons], X).X = [[34, 12]] ;No?- exec([], [nil,emptyp], X).X = [true] ;No?- exec([], [nil,12,cons,emptyp], X).X = [false] ;No?- exec([], [nil,12,cons,34,cons,car], X).X = [34] ;No?- exec([], [nil,12,cons,34,cons,cdr], X).X = [[12]] ;No?- exec([], [12,34,[add],app], X).X = [46] ;No?- e..</description>
<dc:subject>Prolog</dc:subject>
<dc:creator>ether</dc:creator>
<dc:date>2008-05-11T19:01:04+09:00</dc:date>
<content:encoded><![CDATA[
<p>Prolog で簡単なスタック言語を作ってみた。組み込み関数は以下の通り。</p>

<p>- dup: スタックの先頭を複製してスタックに積む<br/>
- swap: スタックの最初の2つを入れ替える<br/>
- pop: スタックの最初の要素を取り除く<br/>
- add: スタックの最初の2つを取り除いてそれらを合計したものを積む<br/>
- if: スタックの先頭の真偽値に応じて2つ目ないし3つ目の関数を実行<br/>
- cons: スタックの最初の要素を2つ目の要素（リスト）にconsする<br/>
- emptyp: スタックの最初の要素を取り除き、それが空リストだったらtrue、それ以外だったらfalseをスタックに置く<br/>
- car: スタックの先頭要素（リスト）を取り除き、そのcarを積む<br/>
- cdr: スタックの先頭要素（リスト）を取り除き、そのcdrを積む<br/>
- app: スタックの先頭要素（関数）を適用する<br/>
- papp: スタックの先頭要素（関数）を2つ目の要素にだけ部分適用する</p>

これが実行例。

<pre><samp>?- exec([], [12,dup], X).

X = [12, 12] ;

No
?- exec([], [12,34,swap], X).

X = [12, 34] ;

No
?- exec([], [12,34,pop], X).

X = [12] ;

No
?- exec([], [12,34,add], X).

X = [46] ;

No
?- exec([], [12,true,[dup,add],[34,swap],if], X).

X = [24] ;

No
?- exec([], [12,false,[dup,add],[34,swap],if], X).

X = [12, 34] ;

No
?- exec([], [nil,12,cons,34,cons], X).

X = [[34, 12]] ;

No
?- exec([], [nil,emptyp], X).

X = [true] ;

No
?- exec([], [nil,12,cons,emptyp], X).

X = [false] ;

No
?- exec([], [nil,12,cons,34,cons,car], X).

X = [34] ;

No
?- exec([], [nil,12,cons,34,cons,cdr], X).

X = [[12]] ;

No
?- exec([], [12,34,[add],app], X).

X = [46] ;

No
?- exec([], [12,34,[add],papp], X).

X = [[34, add], 12] ;

No
?- exec([], [12,34,[add],papp,app], X).

X = [46] ;

No
?- exec([], [12,34,[add],papp,papp], X).

X = [[12, 34, add]] ;

No
?- exec([], [12,34,[add],papp,papp,app], X).

X = [46] ;

No</samp></pre>

<p>実装コード。非常にシンプルで、殆ど仕様そのまま。Prolog すごい。</p>

<pre><code>exec(S, [], S).
exec(S1, [H|T], S2) :- value(H,I), !, exec([I|S1], T, S2).
exec(S1, [H|T], S2) :-
  P =.. [H, S1, S3],
  call(P),
  exec(S3, T, S2).

value(X,X) :- number(X).
value(X,X) :- is_list(X).
value(true,true).
value(false,false).
value(nil,[]).

dup([H|T],[H,H|T]).
swap([X,Y|Z],[Y,X|Z]).
pop([H|T],T).
add([X,Y|Z], [S|Z]) :- S is X+Y.
if([_,X,true|Y], Z) :- exec(Y,X,Z).
if([X,_,false|Y], Z) :- exec(Y,X,Z).
cons([X,Y|Z],[[X|Y]|Z]).
emptyp([[]|T],[true|T]) :- !.
emptyp([_|T],[false|T]).
car([[X|Y]|Z],[X|Z]).
cdr([[X|Y]|Z],[Y|Z]).
app([X|Y],Z) :- exec(Y, X, Z).
papp([X,Y|Z], [[Y|X]|Z]).</code></pre><a name="more"></a>
]]></content:encoded>
</item>
<item rdf:about="http://rainyday.blog.so-net.ne.jp/2008-05-11">
<title>オブジェクト指向言語の実装 (EOPL の 5.4.1)</title>
<link>http://rainyday.blog.so-net.ne.jp/2008-05-11</link>
<description>EOPL の続き。また進路を変更して今度は第5章のオブジェクト指向言語の実装をやることにした。ここでは4通りの実装を紹介しているのだけどまずは 5.4.1 A Simple Implementation から。この実装ではオブジェクトを part というデータ型のリストとして表現する。(define-datatype part part?  (a-part    (class-name symbol?)    (fields vector?)))一つの part はクラス名とフィールドの配列を持っている。継承されたクラスをインスタンス化した場合、例えば c1 を継承した c2 をインスタンス化した場合は part のリストの car が c2 の part で、c2 に固有ないしはオーバーライドしたフィールドが入り、リストの cadr には c1 のフィールドが入る。メソッドを呼び出す場合はこの part のリストを元に環境を作ってやり、その環境の下でメソッドの式を評価する。この環境にはさらにメソッドの引数と self と super が見えるようにしてやる。ただ本の通りに実装するのも単調なのでちょっと自分好みの改変を加えた。EOPL の元の言語ではオブジェクトの初期化を世の多くのオブジェクト指向言語と同様にコンストラクタメソッドの呼び出しで行っている。たとえばこんな風だ。class c1 extends object  field i  field j  method initialize (x)    set i = x    set j = -(0,x)  end  method ...（以下略）この仕様の悪いところは、まずコンストラクタメソッドを書くというのがめんどくさい。次にコンストラクタメソッドの中で初期化を忘れるということを誘発しやすい。これは C++ だと未初期化な値を生む。Java やその他の言語では型ごとの初期値が存在するので問題は小さくなっているけど初期値のままであるのが意図したことなのかどうかは不明だ。というわけでこういう風に書くようにした。class c1(x) extends object()  field i = x  field j = -(0,x)  method ...（以下略）こうすると field の右辺を書くことが強制される。これは OCaml 風で Scala 風でもあ..</description>
<dc:subject>Lisp</dc:subject>
<dc:creator>ether</dc:creator>
<dc:date>2008-05-11T14:10:34+09:00</dc:date>
<content:encoded><![CDATA[
<p>EOPL の続き。また進路を変更して今度は第5章のオブジェクト指向言語の実装をやることにした。ここでは4通りの実装を紹介しているのだけどまずは 5.4.1 A Simple Implementation から。</p>

<p>この実装ではオブジェクトを part というデータ型のリストとして表現する。</p>

<pre><code>(define-datatype part part?
  (a-part
    (class-name symbol?)
    (fields vector?)))</code></pre>

<p>一つの part はクラス名とフィールドの配列を持っている。継承されたクラスをインスタンス化した場合、例えば c1 を継承した c2 をインスタンス化した場合は part のリストの car が c2 の part で、c2 に固有ないしはオーバーライドしたフィールドが入り、リストの cadr には c1 のフィールドが入る。</p>

<p>メソッドを呼び出す場合はこの part のリストを元に環境を作ってやり、その環境の下でメソッドの式を評価する。この環境にはさらにメソッドの引数と self と super が見えるようにしてやる。</p>

<p>ただ本の通りに実装するのも単調なのでちょっと自分好みの改変を加えた。EOPL の元の言語ではオブジェクトの初期化を世の多くのオブジェクト指向言語と同様にコンストラクタメソッドの呼び出しで行っている。たとえばこんな風だ。</p>

<pre><code>class c1 extends object
  field i
  field j
  method initialize (x)
    set i = x
    set j = -(0,x)
  end
  method ...（以下略）</code></pre>

<p>この仕様の悪いところは、まずコンストラクタメソッドを書くというのがめんどくさい。次にコンストラクタメソッドの中で初期化を忘れるということを誘発しやすい。これは C++ だと未初期化な値を生む。Java やその他の言語では型ごとの初期値が存在するので問題は小さくなっているけど初期値のままであるのが意図したことなのかどうかは不明だ。</p>

<p>というわけでこういう風に書くようにした。</p>

<pre><code>class c1(x) extends object()
  field i = x
  field j = -(0,x)
  method ...（以下略）</code></pre>

<p>こうすると field の右辺を書くことが強制される。これは OCaml 風で Scala 風でもある（もっとも、どちらの言語もこの形のコンストラクタしかサポートしないということではない）。コンストラクタのオーバーロードをサポートする場合は結構面倒なことになるけど今回の言語では元々オーバーロードは存在しない。</p>

<p>継承するときにはクラス定義のところでスーパークラスの実引数を与えてやる。</p>

<pre><code>class c1(x,y) extends object()
  field i = x
  field j = y
class c2(z) extends c1(+(z,1),777)
  field k = z
new c2(12)</code></pre>

<p>これは ((a-part c2 #(12)) (a-part c1 #(13 777))) という構造になる。</p>

<p>こういう仕様にしたことで、新たな決め事が発生する。スーパークラスに対する引数の式を評価するときと field の右辺の式を評価するときにそれぞれの環境をどうするか。</p>

<p>スーパークラスに対する引数の式を評価するときはサブクラスのコンストラクタの引数（上で言う c2(z) の z）だけを環境として持つ。これはこれで問題ないだろう。</p>

<p>field の右辺の式を評価するときにはそれに加えて self や他のフィールドや super を見えるようにすべきかどうか。スーパークラスは先に初期化されているので super は問題ないように思われる。self や他のフィールドはどうか。先に初期化されたフィールドを使って別のフィールドを初期化するのは計算の節約に便利なときもあるかも知れないが、未初期化なフィールドを使用してしまう恐れもある。元々未初期化問題を改善したかったのでこれはやや片手落ちだ。</p>

<p>OCaml と Scala を参考にしてみよう。OCaml では self や同じクラスの別のフィールドの参照は禁止されているようだ。</p>

<pre><samp># class c1 x = object(self)
    val i = x
    val j = self#get_i()
    method get_i() = i
  end;;
The instance variable self
cannot be accessed from the definition of another instance variable
# class c1 x = object
    val i = x
    val j = i
  end;;
The instance variable i
cannot be accessed from the definition of another instance variable</samp></pre>

<p>Scala ではあるフィールドを別のフィールドを使って初期化できる。</p>

<pre><samp>scala> class C1(x: Int) {
     |   val i = x
     |   val j = i
     | }
defined class C1

scala> val c1 = new C1(123)
c1: C1 = C1@18a2977

scala> (c1.i, c1.j)
res3: (Int, Int) = (123,123)</samp></pre>

<p>順序が逆転すると初期値を使うことになる。</p>

<pre><samp>scala> class C2(x: Int) {
     |   val i = j
     |   val j = x
     | }
defined class C2

scala> val c2 = new C2(456)
c2: C2 = C2@6d0085

scala> (c2.i, c2.j)
res4: (Int, Int) = (0,456)</samp></pre>

<p>循環が起きた場合はどちらも初期値になる。</p>

<pre><samp>scala> class C3(x: Int) {
     |   val i: Int = j
     |   val j: Int = i
     | }
defined class C3

scala> val c3 = new C3(789)
c3: C3 = C3@197f158

scala> (c3.i, c3.j)
res5: (Int, Int) = (0,0)</samp></pre>

<p>メソッドを介して循環させた場合も同様。</p>

<pre><samp>scala> class C4(x: Int) {
     |   def m(): Int = i
     |   val i: Int = m()
     | }
defined class C4

scala> (new C4(135)).m()
res7: Int = 0</samp></pre>

<p>というわけで OCaml に近い仕様にしました。それにしても C++ とか Java でも初期化/コンストラクタ周りの仕様ってややこしい問題の温床な気がする。</p>

<p>完成した言語での REPL 例は以下のような感じ。ソースは後掲。</p>

<pre><samp>class oddeven() extends object()
  method even(n) if n then send self odd(sub1(n)) else 1
  method odd(n) if n then send self even(sub1(n)) else 0
let o1 = new oddeven()
in send o1 odd(13)
-->1

class interior(l,r) extends object()
  field left = l
  field right = r
  method sum() +(send left sum(), send right sum())
class leaf(v) extends object()
  field value = v
  method sum() value
let o1 = new interior(
           new interior(
             new leaf(3),
             new leaf(4)),
           new leaf(5))
in send o1 sum()
-->12

class point(initx,inity) extends object()
  field x = initx
  field y = inity
  method move(dx,dy)
    let d = set x = +(x,dx) in
    let d = set y = +(y,dy) in 0
  method getx() x
  method gety() y
class colorpoint(initx,inity,initcolor) extends point(initx,inity)
  field color = initcolor
  method setcolor(c) set color = c
  method getcolor() color
let o1 = new colorpoint(3,4,172) in
send o1 getcolor()
-->172

class c1() extends object()
  method m1() send self m2()
  method m2() 13
class c2() extends c1()
  method m1() 22
  method m2() 23
  method m3() super m1()
class c3() extends c2()
  method m1() 32
  method m2() 33
let o3 = new c3() in
send o3 m3()
-->33</samp></pre>

<p>ソース。EOPL ではクロージャとかリスト処理プリミティブとか begin 式がある前提なんだけどブログに載せるために削った。あと構造体のアクセサ的な関数をいっぱい定義しなければならなかったので初めてマクロを書いた。これにより Gauche 依存になってしまっている。</p><a name="more"></a><pre><code>(load "r5rs.scm")
(load "define-datatype.scm")
(load "sllgen.scm")
(use srfi-1)

; reference

(define-datatype reference reference?
  (a-ref
    (position integer?)
    (vec vector?)))

(define primitive-deref
  (lambda (ref)
    (cases reference ref
      (a-ref (pos vec) (vector-ref vec pos)))))

(define primitive-setref!
  (lambda (ref val)
    (cases reference ref
      (a-ref (pos vec) (vector-set! vec pos val)))))

(define deref
  (lambda (ref)
    (primitive-deref ref)))

(define setref!
  (lambda (ref val)
    (primitive-setref! ref val)))

; boolean

(define true-value (lambda () 1))
(define false-value (lambda () 0))
(define true-value?
  (lambda (x)
    (not (= 0 x))))

; environment

(define-datatype environment environment?
  (empty-env-record)
  (extended-env-record
    (syms (list-of symbol?))
    (vals vector?)
    (env environment?))
  )

(define empty-env
  (lambda ()
    (empty-env-record)))

(define extend-env
  (lambda (syms vals env)
    (extended-env-record syms (list->vector vals) env)))

(define extend-env-refs
  (lambda (syms vec env)
    (extended-env-record syms vec env)))

(define apply-env
  (lambda (env sym)
    (deref (apply-env-ref env sym))))

(define apply-env-ref
  (lambda (env sym)
    (cases environment env
      (empty-env-record ()
        (eopl:error 'apply-env "No binding for ~s" sym))
      (extended-env-record (syms vals env)
        (let ((pos (list-find-position sym syms)))
          (if (number? pos)
            (a-ref pos vals)
            (apply-env-ref env sym)))))))

(define list-find-position
  (lambda (sym los)
    (list-index (lambda (sym1) (eqv? sym1 sym)) los)))

(define list-index
  (lambda (pred ls)
    (cond
      ((null? ls) #f)
      ((pred (car ls)) 0)
      (else (let ((list-index-r (list-index pred (cdr ls))))
              (if (number? list-index-r)
                (+ list-index-r 1)
                #f))))))

; interpreter

(define eval-program
  (lambda (pgm)
    (cases program pgm
      (a-program (c-decls exp)
        (elaborate-class-decls! c-decls)
        (eval-expression exp (init-env))))))

(define eval-expression
  (lambda (exp env)
    (cases expression exp
      (lit-exp (datum) datum)
      (var-exp (id) (apply-env env id))
      (primapp-exp (prim rands)
        (let ((args (eval-rands rands env)))
          (apply-primitive prim args)))
      (if-exp (test-exp true-exp false-exp)
        (if (true-value? (eval-expression test-exp env))
          (eval-expression true-exp env)
          (eval-expression false-exp env)))
      (let-exp (ids rands body)
        (let ((args (eval-rands rands env)))
          (eval-expression body (extend-env ids args env))))
      (varassign-exp (id rhs-exp)
        (begin
          (setref!
            (apply-env-ref env id)
            (eval-expression rhs-exp env))
          1))
      (method-app-exp (obj-exp method-name rands)
        (let ((args (eval-rands rands env))
              (obj (eval-expression obj-exp env)))
          (find-method-and-apply
            method-name (object->class-name obj) obj args)))
      (super-call-exp (method-name rands)
        (let ((args (eval-rands rands env))
              (obj (apply-env env 'self)))
          (find-method-and-apply
            method-name (apply-env env '%super) obj args)))
      (new-object-exp (class-name rands)
        (let ((args (eval-rands rands env)))
          (new-object class-name args)))
      )))

(define eval-rands
  (lambda (rands env)
    (map (lambda (x) (eval-rand x env)) rands)))

(define eval-rand
  (lambda (rand env)
    (eval-expression rand env)))

(define apply-primitive
  (lambda (prim args)
    (cases primitive prim
      (add-prim () (+ (car args) (cadr args)))
      (subtract-prim () (- (car args) (cadr args)))
      (mult-prim () (* (car args) (cadr args)))
      (incr-prim () (+ (car args) 1))
      (decr-prim () (- (car args) 1))
      (eq?-prim () (if (eq? (car args) (cadr args)) (true-value) (false-value)))
      )))

; OO-part, a simple implementation

(define the-class-env '())

(define-datatype part part?
  (a-part
    (class-name symbol?)
    (fields vector?)))

(define elaborate-class-decls!
  (lambda (c-decls)
    (set! the-class-env c-decls)))

(define new-object
  (lambda (class-name args)
    (if (eqv? class-name 'object)
      '()
      (let*
        ((c-decl (lookup-class class-name))
         (super-name (class-decl->super-name c-decl))
         (obj (cons (make-first-part c-decl)
                    (new-object
                      super-name
                      (eval-rands
                        (class-decl->real-params c-decl)
                        (extend-env
                          (class-decl->formal-params c-decl)
                          args
                          (empty-env))))))
         (env
           (extend-env
             (cons '%super    (class-decl->formal-params c-decl))
             (cons super-name args)
             (build-field-env
               (view-object-as obj class-name)))))
        (for-each
          (lambda (id rhs-exp)
            (setref!
              (apply-env-ref env id)
              (eval-expression rhs-exp env)))
          (class-decl->field-ids  c-decl)
          (class-decl->field-vals c-decl))
        obj))))

(define make-first-part
  (lambda (c-decl)
    (a-part
      (class-decl->class-name c-decl)
      (make-vector (length (class-decl->field-ids c-decl))))))

(define find-method-and-apply
  (lambda (m-name host-name self args)
    (if (eqv? host-name 'object)
      (eopl:error 'find-method-and-apply
        "No method for ~s" m-name)
      (let ((m-decl (lookup-method-decl m-name
                      (class-name->method-decls host-name))))
        (if (method-decl? m-decl)
          (apply-method m-decl host-name self args)
          (find-method-and-apply
            m-name
            (class-name->super-name host-name)
            self
            args))))))

(define apply-method
  (lambda (m-decl host-name self args)
    (let ((ids (method-decl->ids m-decl))
          (body (method-decl->body m-decl))
          (super-name (class-name->super-name host-name)))
      (eval-expression body
        (extend-env
          (cons '%super    (cons 'self ids))
          (cons super-name (cons self  args))
          (build-field-env
            (view-object-as self host-name)))))))

(define view-object-as
  (lambda (parts class-name)
    (if (eqv? (part->class-name (car parts)) class-name)
      parts
      (view-object-as (cdr parts) class-name))))

(define build-field-env
  (lambda (parts)
    (if (null? parts)
      (empty-env)
      (extend-env-refs
        (part->field-ids (car parts))
        (part->fields    (car parts))
        (build-field-env (cdr parts))))))

(define object->class-name
  (lambda (obj)
    (part->class-name (car obj))))

(define-macro (define-accessor type fields)
  (let ((record (string->symbol (string-append "a-" (symbol->string type)))))
    `(begin
       ,@(map
           (lambda (field)
             (let ((proc-name
                     (string->symbol
                       (string-append
                         (symbol->string type)
                         "->"
                         (symbol->string field)))))
               `(define ,proc-name
                  (lambda (x)
                    (cases ,type x
                      (,record ,fields
                        ,field))))))
           fields))))

; the macro define-accessor defines procedures part->fields ... etc.
(define-accessor part (class-name fields))
(define-accessor class-decl (class-name formal-params super-name real-params
                            field-ids field-vals method-decls))
(define-accessor method-decl (method-name ids body))

(define class-name->super-name
  (lambda (class-name)
    (class-decl->super-name (lookup-class class-name))))

(define class-name->method-decls
  (lambda (class-name)
    (class-decl->method-decls (lookup-class class-name))))

; Exercise 5.1 part->field-ids
(define part->field-ids
  (lambda (p)
    (cases part p
      (a-part (class-name fields)
        (class-decl->field-ids (lookup-class class-name))))))

(define lookup-class
  (lambda (class-name)
    (find
      (lambda (c-decl)
        (eqv? (class-decl->class-name c-decl) class-name))
      the-class-env)))

(define lookup-method-decl
  (lambda (m-name m-decls)
    (find
      (lambda (m-decl)
        (eqv? (method-decl->method-name m-decl) m-name))
      m-decls)))

; etc

(define init-env (lambda () (empty-env)))

(define scanner-spec
  '((white-sp
      (whitespace) skip)
    (comment
      ("%" (arbno (not #\newline))) skip)
    (identifier
      (letter (arbno (or letter digit "?"))) symbol)
    (number
      (digit (arbno digit)) number)))

(define grammar
  '((program
      ((arbno class-decl) expression)
      a-program)
    (class-decl
      ("class" identifier "(" (separated-list identifier ",") ")"
       "extends" identifier "(" (separated-list expression ",") ")"
       (arbno "field" identifier "=" expression)
       (arbno method-decl))
      a-class-decl)
    (method-decl
      ("method" identifier "(" (separated-list identifier ",") ")" expression)
      a-method-decl)
    (expression
      (number)
      lit-exp)
    (expression
      (identifier)
      var-exp)
    (expression
      (primitive "(" (separated-list expression ",") ")" )
      primapp-exp)
    (expression
      ("if" expression "then" expression "else" expression)
      if-exp)
    (expression
      ("let" (arbno identifier "=" expression) "in" expression)
      let-exp)
    (expression
      ("set" identifier "=" expression)
      varassign-exp)
    (expression
      ("new" identifier "(" (separated-list expression ",") ")")
      new-object-exp)
    (expression
      ("send" expression identifier "(" (separated-list expression ",") ")")
      method-app-exp)
    (expression
      ("super" identifier "(" (separated-list expression ",") ")")
      super-call-exp)
    (primitive ("+") add-prim)
    (primitive ("-") subtract-prim)
    (primitive ("*") mult-prim)
    (primitive ("add1") incr-prim)
    (primitive ("sub1") decr-prim)
    (primitive ("eq?") eq?-prim)
    ))

(define scan&amp;parse
  (sllgen:make-string-parser
    scanner-spec
    grammar))

(sllgen:make-define-datatypes scanner-spec grammar)

(define run
  (lambda (string)
    (eval-program
      (scan&amp;parse string))))

(define read-eval-print
  (sllgen:make-rep-loop "-->" eval-program
    (sllgen:make-stream-parser
      scanner-spec
      grammar)))</code></pre>
]]></content:encoded>
</item>
<item rdf:about="http://rainyday.blog.so-net.ne.jp/2008-05-09">
<title>Prolog で油売り算（幅優先探索）</title>
<link>http://rainyday.blog.so-net.ne.jp/2008-05-09</link>
<description>Prolog で幅優先探索をする方法を覚えたので [1] の油売り算を幅優先探索で解いてみた。（1年遅れですが）Prolog で素直に組むと深さ優先探索を行うことになるけど、アジェンダをリストとして明示的に持って findall で次の状態のリストを得て後ろに追加してやれば幅優先探索を行うことができる。アジェンダを作る分だけコードはめんどくさい。abura(Ac,Bc,Cc,Path) :-  move(Ac,Bc,Cc,[[[Ac,0,0]]],Trail),  reverse(Trail,Path).amount(Src,Dest,Cap,Amt) :- Src &amp;gt; 0, Dest &amp;lt; Cap, Src &amp;lt; Cap - Dest, !, Amt = Src.amount(Src,Dest,Cap,Amt) :- Src &amp;gt; 0, Dest &amp;lt; Cap, Amt is Cap - Dest.successor(Ac,Bc,Cc,[A,B,C],[A1,B1,C]) :- amount(A,B,Bc,Amt), A1 is A-Amt, B1 is B+Amt.successor(Ac,Bc,Cc,[A,B,C],[A1,B,C1]) :- amount(A,C,Cc,Amt), A1 is A-Amt, C1 is C+Amt.successor(Ac,Bc,Cc,[A,B,C],[A1,B1,C]) :- amount(B,A,Ac,Amt), B1 is B-Amt, A1 is A+Amt.successor(Ac,Bc,Cc,[A,B,C],[A,B1,C1]) :- amount(B,C,Cc,Amt), B1 is B-Amt, C1 is C+Amt.successor(Ac,Bc,Cc,[A,B,C],[A1,B,C1]) :- amount(C,A,Ac,Amt), C1 is C-Amt, A1 is A+Amt.successor(Ac,Bc,Cc,[A,B,C],[A,B1,C1]) :- amount(C,B,Bc,Amt), C1 is C-Amt, B1 is B+Amt.is_goal([Half,Half,0]).is_goal([Half,0,Half]).is_goal([0,Half,Half]).move(_,_,_,Agenda,Trai..</description>
<dc:subject>Prolog</dc:subject>
<dc:creator>ether</dc:creator>
<dc:date>2008-05-09T15:55:03+09:00</dc:date>
<content:encoded><![CDATA[
<p>Prolog で幅優先探索をする方法を覚えたので [1] の油売り算を幅優先探索で解いてみた。（1年遅れですが）</p>

<p>Prolog で素直に組むと深さ優先探索を行うことになるけど、アジェンダをリストとして明示的に持って findall で次の状態のリストを得て後ろに追加してやれば幅優先探索を行うことができる。アジェンダを作る分だけコードはめんどくさい。</p>

<pre><code>abura(Ac,Bc,Cc,Path) :-
  move(Ac,Bc,Cc,[[[Ac,0,0]]],Trail),
  reverse(Trail,Path).

amount(Src,Dest,Cap,Amt) :- Src > 0, Dest &lt; Cap, Src &lt; Cap - Dest, !, Amt = Src.
amount(Src,Dest,Cap,Amt) :- Src > 0, Dest &lt; Cap, Amt is Cap - Dest.

successor(Ac,Bc,Cc,[A,B,C],[A1,B1,C]) :- amount(A,B,Bc,Amt), A1 is A-Amt, B1 is B+Amt.
successor(Ac,Bc,Cc,[A,B,C],[A1,B,C1]) :- amount(A,C,Cc,Amt), A1 is A-Amt, C1 is C+Amt.
successor(Ac,Bc,Cc,[A,B,C],[A1,B1,C]) :- amount(B,A,Ac,Amt), B1 is B-Amt, A1 is A+Amt.
successor(Ac,Bc,Cc,[A,B,C],[A,B1,C1]) :- amount(B,C,Cc,Amt), B1 is B-Amt, C1 is C+Amt.
successor(Ac,Bc,Cc,[A,B,C],[A1,B,C1]) :- amount(C,A,Ac,Amt), C1 is C-Amt, A1 is A+Amt.
successor(Ac,Bc,Cc,[A,B,C],[A,B1,C1]) :- amount(C,B,Bc,Amt), C1 is C-Amt, B1 is B+Amt.

is_goal([Half,Half,0]).
is_goal([Half,0,Half]).
is_goal([0,Half,Half]).

move(_,_,_,Agenda,Trail) :-
  Agenda = [FirstPath|RestPaths],
  FirstPath = [State|_],
  is_goal(State),
  Trail = FirstPath.
move(Ac,Bc,Cc,Agenda,Trail) :-
  Agenda = [FirstPath|RestPaths],
  FirstPath = [State|Past],
  findall([Succ,State|Past],
    (successor(Ac,Bc,Cc,State,Succ),\+member(Succ,Past)),
    SuccPaths),
  /*append(SuccPaths,RestPaths,NewAgenda),*/ /* depth-first */
  append(RestPaths,SuccPaths,NewAgenda), /* breadth-first */
  move(Ac,Bc,Cc,NewAgenda,Trail).</code></pre>

<p>move の中の単一化は述語の引数のところでパターンマッチさせればもっと簡潔になるはずだけど、頭がついていかないので冗長な書き方にした。あと Prolog には @ とか as にあたるものは無いんだろうか。</p>

<p>実行結果。幅優先なので短い順に答えが出る。SWI-Prolog では解の表示が省略されたときは w を押すと全部表示してくれる。</p>

<pre><samp>?- abura(10,7,3,Answer).

Answer = [[10, 0, 0], [3, 7, 0], [3, 4, 3], [6, 4, 0], [6, 1, 3], [9, 1, 0], [9, 0|...], [2|...], [...|...]|...] [write]

Answer = [[10, 0, 0], [3, 7, 0], [3, 4, 3], [6, 4, 0], [6, 1, 3], [9, 1, 0], [9, 0, 1], [2, 7, 1], [2, 5, 3], [5, 5, 0]] ;

Answer = [[10, 0, 0], [7, 0, 3], [7, 3, 0], [4, 3, 3], [4, 6, 0], [1, 6, 3], [1, 7, 2], [8, 0, 2], [8, 2, 0], [5, 2, 3], [5, 5, 0]] ;

Answer = [[10, 0, 0], [7, 0, 3], [0, 7, 3], [3, 7, 0], [3, 4, 3], [6, 4, 0], [6, 1, 3], [9, 1, 0], [9, 0, 1], [2, 7, 1], [2, 5, 3], [5, 5, 0]] ;

Answer = [[10, 0, 0], [7, 0, 3], [7, 3, 0], [3, 7, 0], [3, 4, 3], [6, 4, 0], [6, 1, 3], [9, 1, 0], [9, 0, 1], [2, 7, 1], [2, 5, 3], [5, 5, 0]] ;

Answer = [[10, 0, 0], [3, 7, 0], [0, 7, 3], [7, 0, 3], [7, 3, 0], [4, 3, 3], [4, 6, 0], [1, 6, 3], [1, 7, 2], [8, 0, 2], [8, 2, 0], [5, 2, 3], [5, 5, 0]] ;

Answer = [[10, 0, 0], [3, 7, 0], [3, 4, 3], [7, 0, 3], [7, 3, 0], [4, 3, 3], [4, 6, 0], [1, 6, 3], [1, 7, 2], [8, 0, 2], [8, 2, 0], [5, 2, 3], [5, 5, 0]] ;

Answer = [[10, 0, 0], [3, 7, 0], [3, 4, 3], [0, 7, 3], [7, 0, 3], [7, 3, 0], [4, 3, 3], [4, 6, 0], [1, 6, 3], [1, 7, 2], [8, 0, 2], [8, 2, 0], [5, 2, 3], [5, 5, 0]] ;

Answer = [[10, 0, 0], [7, 0, 3], [7, 3, 0], [4, 3, 3], [0, 7, 3], [3, 7, 0], [3, 4, 3], [6, 4, 0], [6, 1, 3], [9, 1, 0], [9, 0, 1], [2, 7, 1], [2, 5, 3], [5, 5, 0]] ;

Answer = [[10, 0, 0], [7, 0, 3], [7, 3, 0], [4, 3, 3], [4, 6, 0], [3, 7, 0], [3, 4, 3], [6, 4, 0], [6, 1, 3], [9, 1, 0], [9, 0, 1], [2, 7, 1], [2, 5, 3], [5, 5, 0]] ;

Answer = [[10, 0, 0], [3, 7, 0], [3, 4, 3], [6, 4, 0], [6, 1, 3], [7, 0, 3], [7, 3, 0], [4, 3, 3], [4, 6, 0], [1, 6, 3], [1, 7, 2], [8, 0, 2], [8, 2, 0], [5, 2, 3], [5, 5, 0]] ;

Answer = [[10, 0, 0], [3, 7, 0], [3, 4, 3], [6, 4, 0], [6, 1, 3], [0, 7, 3], [7, 0, 3], [7, 3, 0], [4, 3, 3], [4, 6, 0], [1, 6, 3], [1, 7, 2], [8, 0, 2], [8, 2, 0], [5, 2, 3], [5, 5, 0]] ;

Answer = [[10, 0, 0], [7, 0, 3], [7, 3, 0], [4, 3, 3], [4, 6, 0], [1, 6, 3], [0, 7, 3], [3, 7, 0], [3, 4, 3], [6, 4, 0], [6, 1, 3], [9, 1, 0], [9, 0, 1], [2, 7, 1], [2, 5, 3], [5, 5, 0]] ;

Answer = [[10, 0, 0], [7, 0, 3], [7, 3, 0], [4, 3, 3], [4, 6, 0], [1, 6, 3], [1, 7, 2], [3, 7, 0], [3, 4, 3], [6, 4, 0], [6, 1, 3], [9, 1, 0], [9, 0, 1], [2, 7, 1], [2, 5, 3], [5, 5, 0]] ;

Answer = [[10, 0, 0], [3, 7, 0], [3, 4, 3], [6, 4, 0], [6, 1, 3], [9, 1, 0], [9, 0, 1], [7, 0, 3], [7, 3, 0], [4, 3, 3], [4, 6, 0], [1, 6, 3], [1, 7, 2], [8, 0, 2], [8, 2, 0], [5, 2, 3], [5, 5, 0]] ;

Answer = [[10, 0, 0], [7, 0, 3], [7, 3, 0], [4, 3, 3], [4, 6, 0], [1, 6, 3], [1, 7, 2], [0, 7, 3], [3, 7, 0], [3, 4, 3], [6, 4, 0], [6, 1, 3], [9, 1, 0], [9, 0, 1], [2, 7, 1], [2, 5, 3], [5, 5, 0]] ;

Answer = [[10, 0, 0], [7, 0, 3], [7, 3, 0], [4, 3, 3], [4, 6, 0], [1, 6, 3], [1, 7, 2], [8, 0, 2], [8, 2, 0], [3, 7, 0], [3, 4, 3], [6, 4, 0], [6, 1, 3], [9, 1, 0], [9, 0, 1], [2, 7, 1], [2, 5, 3], [5, 5, 0]] ;

Answer = [[10, 0, 0], [3, 7, 0], [3, 4, 3], [6, 4, 0], [6, 1, 3], [9, 1, 0], [9, 0, 1], [2, 7, 1], [0, 7, 3], [7, 0, 3], [7, 3, 0], [4, 3, 3], [4, 6, 0], [1, 6, 3], [1, 7, 2], [8, 0, 2], [8, 2, 0], [5, 2, 3], [5, 5, 0]] ;

Answer = [[10, 0, 0], [3, 7, 0], [3, 4, 3], [6, 4, 0], [6, 1, 3], [9, 1, 0], [9, 0, 1], [2, 7, 1], [2, 5, 3], [7, 0, 3], [7, 3, 0], [4, 3, 3], [4, 6, 0], [1, 6, 3], [1, 7, 2], [8, 0, 2], [8, 2, 0], [5, 2, 3], [5, 5, 0]] ;

Answer = [[10, 0, 0], [3, 7, 0], [3, 4, 3], [6, 4, 0], [6, 1, 3], [9, 1, 0], [9, 0, 1], [2, 7, 1], [2, 5, 3], [0, 7, 3], [7, 0, 3], [7, 3, 0], [4, 3, 3], [4, 6, 0], [1, 6, 3], [1, 7, 2], [8, 0, 2], [8, 2, 0], [5, 2, 3], [5, 5, 0]] ;

Answer = [[10, 0, 0], [7, 0, 3], [7, 3, 0], [4, 3, 3], [4, 6, 0], [1, 6, 3], [1, 7, 2], [8, 0, 2], [8, 2, 0], [5, 2, 3], [0, 7, 3], [3, 7, 0], [3, 4, 3], [6, 4, 0], [6, 1, 3], [9, 1, 0], [9, 0, 1], [2, 7, 1], [2, 5, 3], [5, 5, 0]] ;

No</samp></pre>

<p>[1] <a href="http://karetta.jp/article/blog/ll-spirit/033840" target="_blank">http://karetta.jp/article/blog/ll-spirit/033840</a></p><a name="more"></a>
]]></content:encoded>
</item>
<item rdf:about="http://rainyday.blog.so-net.ne.jp/2008-05-07">
<title>抽象化と見立て</title>
<link>http://rainyday.blog.so-net.ne.jp/2008-05-07</link>
<description>注意: この記事で書くことはある種の与太話です。システムだとかライブラリだとか設計においては、2つ以上のよく似たものを（そしてそれほど似ていないものも）統一的に取り扱えるようにするということがしばしば行われる。思うに、その統一化をどのようなスタイルで行うかというところに、設計者の感性がもっとも良く現れる。この統一化には大きく言って二種類の思想があるように思われる。ここでは一方を「抽象化」、もう一方を「見立て」と呼ぶことにする。（なお、ここでは抽象化という言葉の指す意味を狭い形でしか用いていないと思う）前者では、よく似た概念 A と B の取り扱いを統一化するために A と B の共通部分を抽象した概念 C を導入して A と B は C の具体的な現れであるとする。これはしばしば段階的な構造をとる。Java を大して知らない私の印象論によると Java の標準ライブラリはこの種の注意深い設計にあふれている。クラスベースで継承が使えるオブジェクト指向言語はこうした設計を直接にサポートしているともいえる。後者の方法では、同じ状況で「B を A に見立てる」。最も顕著な例は UNIX の「ファイル」だ。そこでは本来ファイルではない多くのものをファイルと見立てて取り扱う。Windows Powershell ではいくつかの概念を旧来より Windows にあった「ドライブ」に見立てて取り扱いを統一化している。例えば PowerShell で環境変数の一覧を取得する方法の一つは cd Env: で Env: に移動して dir コマンドを叩くことだ。このドライブのように見えるものは総称してプロバイダと呼ばれるが、プロバイダに対して cd や dir といったコマンドを従来の用途を超えて使いまわすことができる。Oracle では「ビュー」という本来は背後にテーブルの存在を前提とした概念を使ってパフォーマンス情報などの取得を行う（V$ ビューに対して select 文を発行する）。Common Lisp では with-output-to-string マクロを使うことにより、出力ストリーム書き込み関数で文字列の構築を行うことができる。Ruby の Array クラスは配列をキュー、スタック、集合、連想配列といったものに見立てて使うための多くのメソッドを備えている。見立ての設計はアドホックであり、いびつである。UNIX でファ..</description>
<dc:subject>未分類</dc:subject>
<dc:creator>ether</dc:creator>
<dc:date>2008-05-07T15:54:23+09:00</dc:date>
<content:encoded><![CDATA[
注意: この記事で書くことはある種の与太話です。<br />
<br />
システムだとかライブラリだとか設計においては、2つ以上のよく似たものを（そしてそれほど似ていないものも）統一的に取り扱えるようにするということがしばしば行われる。思うに、その統一化をどのようなスタイルで行うかというところに、設計者の感性がもっとも良く現れる。<br />
<br />
この統一化には大きく言って二種類の思想があるように思われる。ここでは一方を「抽象化」、もう一方を「見立て」と呼ぶことにする。（なお、ここでは抽象化という言葉の指す意味を狭い形でしか用いていないと思う）<br />
<br />
前者では、よく似た概念 A と B の取り扱いを統一化するために A と B の共通部分を抽象した概念 C を導入して A と B は C の具体的な現れであるとする。これはしばしば段階的な構造をとる。Java を大して知らない私の印象論によると Java の標準ライブラリはこの種の注意深い設計にあふれている。クラスベースで継承が使えるオブジェクト指向言語はこうした設計を直接にサポートしているともいえる。<br />
<br />
後者の方法では、同じ状況で「B を A に見立てる」。最も顕著な例は UNIX の「ファイル」だ。そこでは本来ファイルではない多くのものをファイルと見立てて取り扱う。<br />
<br />
Windows Powershell ではいくつかの概念を旧来より Windows にあった「ドライブ」に見立てて取り扱いを統一化している。例えば PowerShell で環境変数の一覧を取得する方法の一つは cd Env: で Env: に移動して dir コマンドを叩くことだ。このドライブのように見えるものは総称してプロバイダと呼ばれるが、プロバイダに対して cd や dir といったコマンドを従来の用途を超えて使いまわすことができる。<br />
<br />
Oracle では「ビュー」という本来は背後にテーブルの存在を前提とした概念を使ってパフォーマンス情報などの取得を行う（V$ ビューに対して select 文を発行する）。<br />
<br />
Common Lisp では with-output-to-string マクロを使うことにより、出力ストリーム書き込み関数で文字列の構築を行うことができる。<br />
<br />
Ruby の Array クラスは配列をキュー、スタック、集合、連想配列といったものに見立てて使うための多くのメソッドを備えている。<br />
<br />
見立ての設計はアドホックであり、いびつである。UNIX でファイル扱いされたものがファイルとしての性質の全てを持っているわけではない。例えばそれは書き込み不能かもしれない。また PowerShell のプロバイダの全てがその下にディレクトリのような階層構造を持っているわけではない。Ruby の Array クラスは集合として取り扱うにはいろいろ中途半端すぎる。<br />
<br />
抽象化は一般性があり、美しく、おそらくは維持しやすい。この性質の一部はおそらく A と B を「公平に」扱ったことによる。<br />
<br />
見立ての方法は A を特別に扱い、B に対して不公平である。A を取り扱う方法はこれまでどおり何のペナルティも無いが、B は A に合わせた、必ずしも B を扱う最適ではないかもしれない方法で取り扱われることになる。（不公平な設計はある意味で公平な設計よりも注意深さが必要となる。特別扱いされるものは「よく使われるもの」で無ければいけないからで、この見極めを誤ると悲惨なことになるだろう）<br />
<br />
一方で「公平」な設計は公平性の代償として A と B を「同じくらい面倒」な手段で取り扱う。Java の入出力ライブラリが貶められる理由は多分これによる（やはり印象論）。ただしこれはあくまで傾向の話で、理屈上は適切なラッパーやエイリアスやシンタクスシュガーといったものによって補うことはできるだろうと思う。<br />
<br />
不公平な設計は対称性を捨てる代わりに、典型的なものをより accessible にし、単純なやり方で扱う。これは多分人間の言語の成り立ちにも少し似ている。<br />
<a name="more"></a>
]]></content:encoded>
</item>
<item rdf:about="http://rainyday.blog.so-net.ne.jp/2008-05-05-1">
<title>Prolog 手習い (2)</title>
<link>http://rainyday.blog.so-net.ne.jp/2008-05-05-1</link>
<description>Prolog が少し分かってきたところで以前途中まで読んでいた The Reasoned Schemer を読み返したらだいぶ見通しがよくなっていた。理解を確かめるために The Reasoned Schemer （以下 TRS）の第1章のコードの一部を Prolog に置き換えてみた。まず対話の10番。run_10(Q) :- fail.run_10 はフェイルしかしないので以下のように問うても解は無い。?- run_10(Q).No11番。?- の Q と run_11 の中の Q は別物だけど共有されるので結果的に run_11 の中の Q の値である t が ?- の Q の値になる。run_11(Q) :- t = Q.?- run_11(Q).Q = t ;No12番。conjunction の中にフェイルするものがあると全体がフェイルする。run_12(Q) :- fail, t = Q.?- run_12(Q).No23番。これは TRS でいうと (run* (q) (fresh (x) (== #t x) (== #t q))) という風に fresh というマクロを使って新しいローカルな変数を導入するところなんだけど Prolog では :- の右側に新たに出てくる変数は常にローカルである。これについては個人的には TRS みたいに明示的に導入するほうが Prolog 風よりも分かりやすいと思う。run_23(Q) :- t = X, t = Q.?- run_23(Q).Q = t ;No26番と27番。= による単一化は左右入れ替えても同じ意味。run_26(Q) :- X = t, t = Q.run_27(Q) :- X = t, Q = t.?- run_26(Q).Q = t ;No?- run_27(Q).Q = t ;No28番。instantiate されていない変数。SWI-Prolog では _Gnnn という形で表示される。run_28(X).?- run_28(X).X = _G157 ;No30番。リスト構造。run_30(R) :- [X, Y] = R.?- run_30(R).R = [_G205, _G208] ;No34番。単一化で矛盾が起こるとフェイルするので解が無い。run_34(Q) :- f = Q, t = Q.?- run_34(Q).No3..</description>
<dc:subject>Prolog</dc:subject>
<dc:creator>ether</dc:creator>
<dc:date>2008-05-05T13:38:28+09:00</dc:date>
<content:encoded><![CDATA[
<p>Prolog が少し分かってきたところで以前途中まで読んでいた The Reasoned Schemer を読み返したらだいぶ見通しがよくなっていた。理解を確かめるために The Reasoned Schemer （以下 TRS）の第1章のコードの一部を Prolog に置き換えてみた。</p>

<p>まず対話の10番。</p>

<pre><code>run_10(Q) :- fail.</code></pre>

<p>run_10 はフェイルしかしないので以下のように問うても解は無い。</p>

<pre><samp>?- run_10(Q).

No</samp></pre>

<p>11番。?- の Q と run_11 の中の Q は別物だけど共有されるので結果的に run_11 の中の Q の値である t が ?- の Q の値になる。</p>

<pre><code>run_11(Q) :- t = Q.</code></pre>

<pre><samp>?- run_11(Q).

Q = t ;

No</samp></pre>

<p>12番。conjunction の中にフェイルするものがあると全体がフェイルする。</p>

<pre><code>run_12(Q) :- fail, t = Q.</code></pre>

<pre><samp>?- run_12(Q).

No</samp></pre>

<p>23番。これは TRS でいうと (run* (q) (fresh (x) (== #t x) (== #t q))) という風に fresh というマクロを使って新しいローカルな変数を導入するところなんだけど Prolog では :- の右側に新たに出てくる変数は常にローカルである。これについては個人的には TRS みたいに明示的に導入するほうが Prolog 風よりも分かりやすいと思う。</p>

<pre><code>run_23(Q) :- t = X, t = Q.</code></pre>

<pre><samp>?- run_23(Q).

Q = t ;

No</samp></pre>

<p>26番と27番。= による単一化は左右入れ替えても同じ意味。</p>

<pre><code>run_26(Q) :- X = t, t = Q.
run_27(Q) :- X = t, Q = t.</code></pre>

<pre><samp>?- run_26(Q).

Q = t ;

No
?- run_27(Q).

Q = t ;

No</samp></pre>

<p>28番。instantiate されていない変数。SWI-Prolog では _Gnnn という形で表示される。</p>

<pre><code>run_28(X).</code></pre>

<pre><samp>?- run_28(X).

X = _G157 ;

No</samp></pre>

<p>30番。リスト構造。</p>

<pre><code>run_30(R) :- [X, Y] = R.</code></pre>

<pre><samp>?- run_30(R).

R = [_G205, _G208] ;

No</samp></pre>

<p>34番。単一化で矛盾が起こるとフェイルするので解が無い。</p>

<pre><code>run_34(Q) :- f = Q, t = Q.</code></pre>

<pre><samp>?- run_34(Q).

No</samp></pre>

<p>35番。矛盾しないなら何回でも = を使ってよい。</p>

<pre><code>run_35(Q) :- f = Q, f = Q.</code></pre>

<pre><samp>?- run_35(Q).

Q = f ;

No</samp></pre>

<p>37番。フレッシュな変数同士の単一化。次に見るように両者は共有される。</p>

<pre><code>run_37(R) :- X = R.</code></pre>

<pre><samp>?- run_37(R).

R = _G157 ;

No</samp></pre>

<p>38番と39番。X と Q を単一化するので X と単一化された値である t が Q の値にもなる。</p>

<pre><code>run_38(Q) :- t = X, X = Q.
run_39(Q) :- X = Q, t = X.</code></pre>

<pre><samp>?- run_38(Q).

Q = t ;

No
?- run_39(Q).

Q = t ;

No</samp></pre>

<p>47番。バックトラック。複数の解が見つかる。最後の fail の行は TRS にあわせたけど書かなくてもよい。これ以降は書かない。</p>

<pre><code>run_47(X) :- olive = X.
run_47(X) :- oil = X.
run_47(X) :- fail.</code></pre>

<pre><samp>?- run_47(X).

X = olive ;

X = oil ;

No</samp></pre>

<p>50番。フェイルするルールは飛ばされる。</p>

<pre><code>run_50(X) :- virgin = X, fail.
run_50(X) :- olive = X.
run_50(X).
run_50(X) :- oil = X.</code></pre>

<pre><samp>?- run_50(X).

X = olive ;

X = _G157 ;

X = oil ;

No</samp></pre>

<p>53番。再びリスト構造。</p>

<pre><code>run_53(R) :- split = X, pea = Y, [X, Y] = R.</code></pre>

<pre><samp>?- run_53(R).

R = [split, pea] ;

No</samp></pre>

<p>54番。さっきの X と Y の組み合わせに複数の解がある。</p>

<pre><code>run_54(R) :- run_54_helper(X, Y), [X, Y] = R.
run_54_helper(X, Y) :- split = X, pea = Y.
run_54_helper(X, Y) :- navy = X, bean = Y.</code></pre>

<pre><samp>?- run_54(R).

R = [split, pea] ;

R = [navy, bean] ;

No</samp></pre>

<p>そして54番の別解。一つの節の中に or があるのはあまり Prolog っぽくない？</p>

<pre><code>run_54_2(R) :- (split = X, pea = Y; navy = X, bean = Y), [X, Y] = R.</code></pre>

<p>58番。これはまず TRS の元のコードを先に挙げる。</p>

<pre><code>(run* (r)
  (fresh (x y z)
    (cond-e
      ((== y x) (fresh (x) (== z x)))
      ((fresh (x) (== y x)) (== z x))
      (else fail))
    (== (cons y (cons z '())) r)))</code></pre>

<p>TRS の fresh マクロはこういう風に任意の場所で新しくローカルな変数を導入できて、例えば4行目の fresh のなかの x は2行目で導入された x とは別であるということがポイント。これは Prolog にどう訳せばいいか？</p>

<p>こうやって新たな述語（run_58_helper_1 と run_58_helper_2）を導入するしかないか。まだ知らないだけで TRS の fresh 相当があったりいないかな。</p>

<pre><code>run_58(R) :- run_58_helper(X, Y, Z), [Y, Z] = R.
run_58_helper(X, Y, Z) :- Y = X, run_58_helper_1(Z).
run_58_helper(X, Y, Z) :- run_58_helper_2(Y), Z = X.
run_58_helper_1(Z) :- Z = X.
run_58_helper_2(Z) :- Y = X.</code></pre>

<pre><samp>?- run_58(R).

R = [_G205, _G208] ;

R = [_G205, _G208] ;

No</samp></pre>

<p>TRS 版だと内側の fresh のなかの z や y が自動的に外側の fresh で導入された z や y を指してくれるけど Prolog 版だとむしろそっちを渡して結び付けてやる必要がある。</p>

<p>上記のような例で外の X と中の X が別だというのは以下のようにするとはっきりする。59番。</p>

<pre><code>run_59(R) :- run_59_helper(X, Y, Z), f = X, [Y, Z] = R.
run_59_helper(X, Y, Z) :- Y = X, run_59_helper_1(Z).
run_59_helper(X, Y, Z) :- run_59_helper_2(Y), Z = X.
run_59_helper_1(Z) :- Z = X.
run_59_helper_2(Y) :- Y = X.</code></pre>

<pre><samp>?- run_59(R).

R = [f, _G208] ;

R = [_G205, f] ;

No</samp></pre>

<p>もし外と内の X が共有されていたら X=Y=Z でどちらも [f, f] になったところ。</p><a name="more"></a>
]]></content:encoded>
</item>
<item rdf:about="http://rainyday.blog.so-net.ne.jp/2008-05-05">
<title>Prolog 手習い (1)</title>
<link>http://rainyday.blog.so-net.ne.jp/2008-05-05</link>
<description>何の脈絡もなく Prolog の勉強を始めた。Amazon のウィッシュリストにいつまでも Clocksin と Mellish の &quot;Programming in Prolog&quot;（第5版）があったのをカートに入れたのだ。いまのところ第4章のカットのところまで読んだのだけど Prolog の動作の仕組みを念入りに解説してくれていて、大変わかりやすい。これまで Prolog に感じていたもやっとがかなり晴れた（やっぱり論理プログラミングとはいっても内部の動きを意識しないと腹に落ちないのか、と思うとちょっと微妙な気分だけど）。ここまでの内容では is と数値計算用の関数がよくわからない。まず数値計算用の + だとかは中置演算子の形をしているだけで 1+1 は +(1,1) とみなせると言っている。だからこう書いておけば+(john, mary).こうなる。?- john + mary.Yes?- X + Y.X = johnY = maryここまではいい。こうした演算子は is と組み合わせると突然数値計算を行う。?- X is 1 + 1.X = 2さっきの + は ML 風に書くと (term * term) -&amp;gt; bool だったんだけど is の右側にくるときは (term * term) -&amp;gt; term になっている。普通の述語を is の右側に書くとエラーになる。なんか気持ち悪いのは、この is の機能はとってつけたようにこうなっているだけなんだろうか。つまり + やら - やらは is の方で特別扱いされていて、ユーザ定義で (term * term) -&amp;gt; term になるようなものを書くことはできないってことでいいのかな。ところで Programming in Prolog は分かりやすいんだけど Prolog の計算の流れを図示する方法は結構苦慮しているように思える。述語の入れ子関係と、バックトラックの選択肢からなるツリーと、計算過程での変数の状況をいっぺんに静的な図で表現できる書き方はないかなあ。バックトラックという「動き」はどうにも頭への収まりが悪い。あと間違いを見つけた。p.56 の左再帰の例でperson(adam).person(X) :- person(Y), mother(X, Y).person(X) :- person(Y), mother(X, Y).pers..</description>
<dc:subject>Prolog</dc:subject>
<dc:creator>ether</dc:creator>
<dc:date>2008-05-05T10:30:56+09:00</dc:date>
<content:encoded><![CDATA[
<p>何の脈絡もなく Prolog の勉強を始めた。Amazon のウィッシュリストにいつまでも Clocksin と Mellish の "Programming in Prolog"（第5版）があったのをカートに入れたのだ。</p>

<p>いまのところ第4章のカットのところまで読んだのだけど Prolog の動作の仕組みを念入りに解説してくれていて、大変わかりやすい。これまで Prolog に感じていたもやっとがかなり晴れた（やっぱり論理プログラミングとはいっても内部の動きを意識しないと腹に落ちないのか、と思うとちょっと微妙な気分だけど）。</p>

<p>ここまでの内容では is と数値計算用の関数がよくわからない。まず数値計算用の + だとかは中置演算子の形をしているだけで 1+1 は +(1,1) とみなせると言っている。</p>

<p>だからこう書いておけば</p>

<pre><code>+(john, mary).</code></pre>

<p>こうなる。</p>

<pre><samp>?- john + mary.

Yes
?- X + Y.

X = john
Y = mary</samp></pre>

<p>ここまではいい。こうした演算子は is と組み合わせると突然数値計算を行う。</p>

<pre><samp>?- X is 1 + 1.

X = 2</samp></pre>

<p>さっきの + は ML 風に書くと (term * term) -> bool だったんだけど is の右側にくるときは (term * term) -> term になっている。
普通の述語を is の右側に書くとエラーになる。なんか気持ち悪いのは、この is の機能はとってつけたようにこうなっているだけなんだろうか。
つまり + やら - やらは is の方で特別扱いされていて、ユーザ定義で (term * term) -> term になるようなものを書くことはできないってことでいいのかな。</p>

<p>ところで Programming in Prolog は分かりやすいんだけど Prolog の計算の流れを図示する方法は結構苦慮しているように思える。
述語の入れ子関係と、バックトラックの選択肢からなるツリーと、計算過程での変数の状況をいっぺんに静的な図で表現できる書き方はないかなあ。
バックトラックという「動き」はどうにも頭への収まりが悪い。</p>

<p>あと間違いを見つけた。p.56 の左再帰の例で</p>

<pre><code>person(adam).
person(X) :- person(Y), mother(X, Y).

person(X) :- person(Y), mother(X, Y).
person(adam).</code></pre>

<p>前者が左再帰で止まらなくて、後者が解決法だって書いてるんだけど逆ですよね。すごい悩んでしまった。（あと mother でいいのかとも）</p>
<a name="more"></a>
]]></content:encoded>
</item>
</rdf:RDF>

