yamicha.com's Blog - Presented by yamicha.com
Blog yamicha.com's Blog - 2017/11 の記事
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30]

yamicha.com's Blog
 諸事情により、現在更新休止中。ご了承ください。もし今後ブログを再開することがあるとすれば、その際にはこのブログスクリプトではなく、新しく開発したものによるかもしれません。
 当ブログ管理者についてはこちらをご参照。
開発魔法(737)
社会問題(733)
お知らせ(11)
質問・バトン回答(15)
ゲスト出演(8)
経済・知的財産(150)
ゲーム開発(182)
[Ada] 勝手に補足
- Note
- 金配りの次の一手


- Endless Ultimate Diary
- 銃世界

漢字バトン
- うるる雑記帳
- 漢字接力棒

ツキアイゲノムバトン
- ブログ@うにうに画像倉庫
- あぶ内閣

縺イ縺セ縺、縺カ縺励ヰ繝医Φ
- 月夜のボヤキ
- 騎士サーラバトン
パスワードを使う
名無し (2012/02/27)


開発者解放裁判
yamicha.com (2010/03/14)
Winnyに関しては、私も「純白」とまでは考えておりませんし、使用し..

開発者解放裁判
通りすがり (2010/03/08)
winnyに関しては「ダウンロードソフト板」なんてところを拠点に開発..

新型インフルエンザの恐怖
いげ太 (2009/11/03)
> C#などの「int Some(int , int)」は、F#では「(int * int) ->..

時効に関する思考
yamicha.com (2009/08/31)
>いげ太さんコメントありがとうございます。手元にドキュメントが少..
Homepage
Blog Top
Normal View
List View
Search
Calendar
Comment List
Trackback List
Blog RSS
Send Trackback
Phone Mode
Administrator
yamicha.com
Blog
るううるる。
Source
法令データ提供システム
FindLaw
Development
Java2 Platform SE 6
Java EE 6 API
MySQL Developer Zone
PHP Reference
MSDN Library
Ada Reference Manual
Objective Caml
Python Documentation
Erlang
Prolog Documents
カテゴリ表示
 カテゴリ 経済・知的財産 に当てはまるもののみを表示します。

 存在する記事 150 件の中から 11-15 件を表示しています。
金配りの次の一手
2009/03/16(Mon)22:33:06
 現在の自民党における屈指の実力者といえば、与謝野氏と考えて間違いないでしょう。この深刻な経済状況において、株に満期があると勘違いするレベルの人間がまっとうな策を打てるわけがありませんし、日銀といった重要な機関は建前上政府とは独立していますので、ここで指揮を取れるのは経済関連ポストをすべて占めている与謝野氏を置いて他にありません。最近はまともな人材が見当たらない状況の自民党だけに、実力者の与謝野氏が次期総裁の主要候補と目されるのも無理はありません。
 確かに、与謝野氏は実力面では問題のない人間であると見られます。麻生氏や安部氏のように実力の伴わない人間が首相のポストに就けば、日本を巻き込んで没落するのは今までの実例から明らかですし、同様の理由から実力のない舛添氏などを据えるのも論外です。選挙前にまたしても首相交代を行うのかは定かではありませんが、それを行うにしても行わないにしても、当面は与謝野氏が中心となって経済対策を進める可能性は高いといえます。
 私は与謝野氏の方針に対して全面的に賛成はしませんが、解散もいつになるか分からない現状において、経済を手当てできる人は実質的にこの人しかいませんので、ある程度の期待をしていました。何より実力者ですし、そもそも選挙管理内閣として発足した一発屋的な麻生内閣にあって、珍しく堅実に物事を遂行するタイプとみられるためです。
 しかしながら、ここで与謝野氏が困ったことを言い出しました。自民党の「政府紙幣・無利子国債発行を検討する議員連盟」なる議連が提言を提出したことを受け、無利子非課税国債と政府紙幣発行を検討するというのです。与謝野氏はもともとこの種の政策に積極的な考えのようですし、状況に応じてそれが必要になる場面も確かに存在するはずですが、私は本政策には賛成できませんし、実行してはいけないと考えています。
 政府紙幣に関しては、まだ無利子非課税国債よりは検討の余地はあります。これはいわば麻薬のようなものですので、下手に使用すると体を壊してしまう恐れがありますが、経済状況の悪い時に限定して十分注意して使用すれば、痛み止めのモルヒネとしても機能するでしょう。応急措置として十分注意して用い、しかもそれを元手に金を配るようなふざけたことをしないのであれば、まだ支持の余地はあります。意図や規模、用途などがはっきりしない以上、現段階ではまだ支持するともしないともいえません。
 しかし、問題は無利子非課税国債です。これは利子がつかない代わりに相続税が非課税となる国債で、与謝野氏によれば「日本は1500兆円以上の個人金融資産があるが、60歳以上に集まっている。本当に必要な人にお金を使えるようにしないといけないという同じ問題意識を持っている」とのことで、無利子非課税国債によって高齢者の多額の資産を国に還流させる意図があるようですが、与謝野氏らしからぬ単なる詭弁にしか見えません。
 相続税と無利子非課税国債については以前の記事で詳しく述べていますので、必要ならそちらを参照していただくとして、ここでは軽くおさらいをしておきましょう。いかなる場合に相続税が発生するかといいますと、まず5000万円が控除され、さらに1000万円に相続人の数を乗じた額を加算し、相続税はこれを越えた部分のみに課税されます。つまり、控除額は少なくとも6000万や7000万、多ければそれ以上となり、それ以下の相続には課税されません。
 さらに、仮にその額を超える相続が発生したとしても、超過分の1000万円までの部分には10%、1000万円から3000万円の部分には15%といった程度の税が加算されるのみで、最大課税率の50%に達するには超過分が3億円以上なくてはなりません。控除の部分は全く非課税ですし、超過部分のうち3億円以内の部分には50%より低い税率しか適用されませんので、50%なる課税は一般人にはそうそう縁のあるものではありません。実質的には、4億も相続してようやく50%の部分が多少出てくる程度でしょうか。
 したがって、大半の人には相続税など無縁のものですし、仮に課税されたとしてもわずかな額に過ぎないといえます。相続税が免除されて利益を得る人がいるとすれば、莫大な資産を持つ一部の人に限られます。格差の拡大が指摘され、派遣切りなどで日本が大きな打撃を受ける中、さらに金持ちを優遇する策が有効とは到底考えられません
 与謝野氏の発言からすれば、一部の富豪が持つ金を無利子国債に変えさせ、それを財源として弱者その他の支援を行う意図なのでしょうが、つまりは金持ちに相続税逃れの手段を提供する策に過ぎないのですから、当然ながら富を分配する機能は低下します。また、国債自体は無利子であっても、国は相続税を受け取れなくなるのですから、迂回献金ならぬ「迂回利払い」が発生するだけに過ぎません
 与謝野氏や政府としては、国債の利払いを抑制したり、あるいは無利子で受けた金を運用して利益を出すなどすれば、相続税がある場合と比べても収支をプラスにできるとの読みがあるものとも考えられます。しかし、仮にその目論見通りになり、格差を推進して金持ちを焼け太らせるような手段で多少の利益が確保できたとしても、これによって富の分配機能は損なわれ、格差の固定化が発生します。
 また、推進派もこれが課税逃れに使われる可能性は認識しているようで、譲渡制限など課税逃れを困難にする制限を設けることも検討されているようですが、そもそも「無利子」なる不利な国債をあえて買おうとする人がなぜ現れるのか分かっているのでしょうか。言うまでもありませんが、これは「相続税免除」のメリットがデメリットを補って余りあるためで、もし買ったところで損害の方が大きいようなら、金持ちはこのようなものを買おうとはしないでしょう。課税逃れも何も、そもそも課税回避をウリにしている国債である点を忘れてはいけません。金持ちがこれを購入するのは、それが金持ちにとって有利な条件であるからに他なりません。一方、本当に課税逃れができないほどの制限を課せば、メリットが存在しなくなりますので、買う人はいなくなります。
 与謝野氏は「個人資産の多くが60歳以上に集中している」点を問題視し、それを「本当に必要な人」に使用することを大義名分にしていますが、前述の通り多額の相続税を払わなければならないのはごく一部の富豪に過ぎません。60歳以上の一部の富豪の財産に課されるべき相続税を免除し、その死後に相続人が莫大な財産を丸々受け取れるようにするのが、「本当に必要な人」に金を分配する手段なのでしょうか。このような政策を実行するのと、1500兆円の大半を持つという60歳以上の一部の富豪に対してしっかり相続税を課すのでは、どちらの方が「本当に必要な人」に金を分配する方法として有利でしょうか。
 さらには、自民党内には「贈与税軽減」の話まで出てきているようです。贈与税も相続税に似た傾向のある税で、こちらは生前の贈与に対して税を課すものです。どうなるかは今後の検討次第とはいえ、これもまた金持ち優遇策の1つとなる可能性があります。格差の固定化などが問題になっている中で、なぜこの期に及んでこのような政策を連発するのか、非常に理解に苦しみます。
 与謝野氏は実力者のはずなのですが、麻生内閣でも経済ポストの重役にありながら、なぜ金配りを何としても阻止しなかったのかも気になります。金配りが日本のためにならないことなど、与謝野氏ともあろう人なら最初から分かっていたはずです。この不況で財政出動の必要性が増し、最大級の補正予算を組まなければならない状況のようですが、法人税などの税収も大幅な下落となっており、早速問題が出ている始末です。金配りで2兆円もの金をドブに捨てていなければ、もう少しはマシになっていたはずなのです。この様子では、金配りの2兆円分の代償は、遠からぬうちに降りかかってくる可能性があります。
 与謝野氏が自民党随一の実力者であり、少なくとも無能すぎる麻生氏の何十倍という能力を持つであろうことには、異論はありません。しかし、その与謝野氏さえこのような政策論を持っているようでは、非常に不安であると言わざるを得ません。少なくとも、以上の疑問に答える程度の説明は欲しいところです。

 C言語には構造体、共用体、列挙の3つの構造があります。構造体は最も多用されるもので、これなくしてCはまともに使えません。クラスの機能限定版のようなものであるため、OOP言語では廃止される傾向にあります。また、C#やDの構造体は名前こそ同じですが、実際には全く意味が違う機能です。共用体は構造体と同類ですが、全要素の格納位置がメモリ上の同じ位置から始まる点が異なります。これはこれで局面によっては便利なのですが、OOPでは無理な共用よりもクラスの継承を用いた方が安全であるため、廃止される傾向にあります。もしOOP言語が普及していなかったら、DOMのノードはunionで実装されていた可能性があります。
 唯一生き残っているといえるのが列挙たるenumで、いわゆる定数のセットとして用いられています。C#のenumは基本的に定数とほとんど変わりませんが、タイプセーフかつOR演算可能などの長所があります。Javaのenumは作られたインスタンスの集合体で、それ自体がメソッドを保持できるため、なかなか便利です。
 ところで、関数型言語には「バリアント」なるものがあります。Haskellにもありましたが、OCamlにも用意されています。これは上記3つを全部足し合わせたような不思議なもので、当然ながらタイプセーフですし、定数としても使用でき、定数に値を持たせるような使い方もでき、共用体のように1つの型に2つ以上の機能を持たせたりもできます。
 最も単純な活用法は、enumとしての使用です。その際には以下のように定義します。
type paradigm = Procedural | Functional | Logical
 これは、paradigm型がProcedural、Functional、Logicalのいずれかを表すものとする宣言です。paradigmの先頭の文字が小文字に、Procedural以下の先頭の文字が大文字になっている点に注目してください。
 受け取り側は、ifなどの条件判定で分岐しても構いませんが、パターンマッチを使用するとより簡単です。
type paradigm = Procedural | Functional | Logical;;

let for_example (p : paradigm) : string = match p with
	| Procedural -> "FORTRAN"
	| Functional -> "OCaml"
	| Logical -> "Prolog";;

Printf.printf "%s" (for_example Functional)
 列挙なり定数なりは使う場面が多いため、これは色々な場面で使用できます。
type sort_type = Asc | Desc;;

let sort (source : int list) (t : sort_type) : int list =
	let rec in_sort source = match source with
		| x :: xs -> List.append (in_sort (List.filter ((>) x) xs))
			(x :: (in_sort (List.filter ((<=) x) xs)))
		| [] -> []
	in match t with
		| Asc -> in_sort source
		| Desc -> List.rev (in_sort source);;

List.iter (Printf.printf "%d ") (sort [5; 1; 9; 3; 6; 2; 8; 7; 4] Asc);;
Printf.printf "\n";;
List.iter (Printf.printf "%d ") (sort [5; 1; 9; 3; 6; 2; 8; 7; 4] Desc)
 これだけならenumと変わりませんが、バリアントの機能は列挙にとどまりません。それぞれにデータを持たせたりもできるため、unionと似たような使い方が可能なのです。さらに、unionでは些細なミスから型安全性を破壊してしまう場合がありますが、バリアントなら安全です。
 以下、intまたはstringのいずれかを取るバリアントです。
type int_or_string = HoldInt of int | HoldString of string;;

let show (d : int_or_string) = match d with
	| HoldInt i -> Printf.printf "%d" i
	| HoldString s -> Printf.printf "%s" s;;

show (HoldString "spam!");;
show (HoldInt 16384)
 バリアントの中身のデータは、上記のようにパターンマッチによって取得できます。unionと違って型安全で、クラスよりも手軽かつキャストやジェネリックを考える必要もないのがありがたいところです。
 また、C#のint?型、JavaのInteger型のように、無効な値をサポートする型を取るような使い方もできます。SQLではINTなど大半のカラム(NOT NULLでないもの)にNULLを格納できますが、OCamlでもバリアントを使えばそのようなものを作成できます。他の言語でいえば、HaskellのMaybeに似ています。
type maybe = Just of int | Nothing;;

(* ゼロ除算なら Nothing を返す *)
let div a b = if b != 0 then Just (a / b) else Nothing;;

let show (d : maybe) = match d with
	| Just i -> Printf.printf "%d\n" i
	| Nothing -> print_string "Nothing\n";;

show (div 10 5);;
show (div 10 0)
 しかし、このmaybeはあまり役に立ちません。機能自体は普遍的なものであるのに、型としてintしか取れないのでは、汎用性に乏しすぎるのです。例えばC#のnull許容型の場合、int?やfloat?など値型はどれもnull許容化できるようになっていますし、HaskellのMaybeも汎用的に使用できます。intだけしか取れないのでは話になりません。
 そこで使用するのが、あの'aです。基本的にはジェネリックと似ているのですが、ジェネリックが「List<Integer>」などと表記されるのとは逆に、OCamlでは「int list」の順番で表記するのが基本ですので、これにならって'aを型名の前に持ってきます。
type 'a maybe = Just of 'a | Nothing;;
 これで汎用的なmaybeのできあがりです。intでもstringでもお好きなものをどうぞ。
 OCamlのデータは基本的に値渡しなのですが、そのような言語にこの種の機能が搭載されているのは非常に助かります。同じく関数型であるHaskellにはバリアントがあり、JavaやC#のクラスは参照渡しが標準で、C++はvoid*など反則的なポインタの扱い方ができるとして、Adaは値渡しの上にこのようなスタイルは用いられません。したがって、例えば連想配列などからデータを取り出したいとして、Javaその他ではデータが存在しなければnullを返せるところを、Adaでは例外を投げるしか方法がありません。関数型、特に純粋なプログラミングが強制または推奨されている言語では、データが値渡しにされる場合が多いため、これは非常にありがたい機能です。

※09/03/21追記
 トラックバックによると、ここの部分が誤解を招くようですので、追記しておきます。
 トラックバック元の方は極めて優れた開発者の方なのですから、「このようなスタイルは用いられません」の書き方で言外の意図を解釈していただきたかったのですが、Adaでは無理と書いているわけではありません。Adaを少しでも書いた方なら、少なくとも「できない」わけではないことはお分かりでしょう。あくまで習慣的な部分に言及しているに過ぎません。ご指摘はありがたいのですが、万一この種の書き方のたびに誰かからご指摘があると、コメントやトラックバック欄があふれてしまいます。
 これは私が自作Ordered_MapをAdaで書いた際の恨み節です。標準ライブラリのOrdered_Mapでは、取得しようとした要素がなければ(nullを返してくるJavaと違って)容赦なく例外を投げてきます。必ず値があると分かっていれば良いのですが、そうでなければ血眼でContainsとElementです。用途に応じた関数を作ってかませるなどの手もありますが、次善の策です。自作の際にはどうにかならないものかと考えましたが、結局例外以外に汎用的な策がなく、標準ライブラリには到底勝てませんでした。下手に「Containsの後にElement」とすると2度のサーチが必要となり(標準ライブラリでは、まずFindをかけてカーソルを取り、No_Elementとの比較の上でElementを使うのが正解でしょうが、Javaよりもう一手間といったところです)、かといって例外を投げるのは、Javaほどの負荷はなさそうですが、手間はしっかりかかるとあって、困り果てました。
 一方、HaskellではMaybeやEitherが標準で用意されており、OCamlではoptionが習慣となっているようですので、このように書いたに過ぎません。個人的にはAdaはかなり好きな言語なのですが、私はハイパースレッディング非搭載ですので、ご指摘のたびに追記や弁解をするのはほぼ不可能です(さらには、この追記に対しての指摘も可能でしょう。仮にご指摘があるとすれば、大体どの辺りが問題となるかはおおむね予想できています)。本当は年末から最近にかけてAda第2部のネタを用意していたのですが、このブログでは当面Adaは扱わない方が良いのかもしれません。

 ところで、OCamlでは演算子のオーバーロードもサポートされています。基本的にはC++やC#などと同じものですが、それらのような一般的な手続き型のオーバーロードとは異なり、OCamlでは演算子を創作しても構いません。演算子というものが存在しないLispはともかくとして、関数型にはOCaml以外にも演算子を自作できるものが多く見受けられます。
 以下の自作演算子「+=」は、ref int型に対して値を加算代入するものです。
let (+=) (a : int ref) (b : int) = a := (!a + b);;

let oper_test = let a = ref 10 in
	a += 20;
	a += 30;
	Printf.printf "%d" !a
 maybeが実装可能な上、自作演算子まで使えるとなれば、これを作らない手はありません。以下のコードをHaskell使いの方々にささげます。
type 'a maybe = Just of 'a | Nothing;;

(* Monad クラスの >>= の機能 *)
let (>>=) (m : 'a maybe) (f : 'a -> 'b maybe) : 'b maybe =
	match m with
		| Just d -> f d
		| Nothing -> Nothing;;

(* ゼロ除算であれば Nothing を返す割り算関数
ゼロ除算でなければ Just int が得られる *)
let div (a : int) (b : int) : int maybe =
	if b != 0 then Just (a / b) else Nothing;;

(* int maybe の表示処理用の関数 *)
let show (m : int maybe) = match m with
	| Just d -> Printf.printf "%d\n" d
	| Nothing -> print_string "Nothing\n";;

(* 使用例 *)
(* A.通常の使用 *)
show (Just 10000 >>= (fun a -> div a 2) >>= (fun a -> div a 5) >>=
	(fun a -> div a 3) >>= (fun a -> div a 4));;
(* 結果 : 83 *)

(* B.途中でエラー *)
show (Just 10000 >>= (fun a -> div a 2) >>= (fun a -> div a 0) >>=
	(fun a -> div a 3) >>= (fun a -> div a 4));;
(* 結果 : Nothing *)
 モナドを使える方にとっては、実に基本的なコードでしょう。ひとたびNothingとなれば、残り全部がNothingです。OCamlは非純粋関数型言語ですし、例外の機能も備えているため、モナドのような奇策を用いなくても何とかなるのですが、そのような使い方はあまり行わないにしても、少なくともAdaのような苦労をする必要がないのは助かります。
 もしバリアントに複数の値を保持させたい場合には、タプルを使用するのが最も簡単です。
type 'a some = A of 'a * 'a | B of 'a;;
 この場合、Aを使用した場合には'a * 'aのタプルとなり、Bであれば'a単独となります。使いどころはやや想像しづらいのですが、バリアントでも合成型は扱えることが分かります。
 また、これも開発においてはお決まりの道具ですが、OCamlにはレコード型も用意されています。規模がさほど大きくなく、しかもその場限りのデータであれば、いわば無名構造体であるタプルでも特に問題は出ませんが、そうでないならレコードが便利です。
 レコードは次のように宣言します。
type person = {name : string; birth : int};;
 意味はほとんど見ての通りでしょう。構造体やレコードの宣言方法など、どの言語でもほとんど似たようなものでしょうが、ややAdaに似た印象を受けます。参考までに、Adaでは以下のようになります。
type person is record
	name : String(1..16);
	birth : Integer;
end record;
 構造体の作成は比較的簡単で、以下のようにするだけです。宣言の際に型名を記述していないのが少々引っかかりますが、そのような仕様なのですから仕方ありません。
{name = "Ada"; birth = 1815}
 構造体は以下のように使用します。
type person = {name : string; birth : int};;

List.iter (fun p -> Printf.printf "%s\t%d\n" p.name p.birth)
	[{name = "Ada"; birth = 1815};
	{name = "Grace"; birth = 1906};
	{name = "Alan"; birth = 1940};
	{name = "Larry"; birth = 1954}];;
 ここで気になるのが、もし同内容のレコードが複数存在したらどうなるかという点です。このような現象はそうそう起こらないはずですが、なにぶん型を指定せずにオブジェクトが作れてしまうのですから、かぶってしまう可能性が全くないとはいえません。例えば、
type person = {name : string; title : string};;
type book = {name : string; title : string};;
 このようなレコードを作成したとしましょう。personの場合、titleは「肩書き」の意味です。bookのnameは著者名、titleは書名です。これらは明らかに別のものですが、型指定なくしては区別する方法がありません。宣言だけならエラーなく受け入れてくれますが、使用した際には一体どうなるのでしょうか。
type person = {name : string; title : string};;
type book = {name : string; title : string};;

let show_person (p : person) = Printf.printf "%s・%s氏\n" p.title p.name;;
let show_book (b : book) = Printf.printf "%s著 %s\n" b.name b.title;;
 4行目でエラーが発生してしまいました。「This expression has type person but is here used with type book」(この式はperson型を持っているが、使われているのはbook型だ)とのことです。どうやら後から登録されたbookによってpersonの要素が上書きされてしまったらしく、p.titleがbookの要素とみなされてしまっているようです。
 さらに悪いことに、OCamlではどうやらレコードの要素は同じ名前を持つだけで危ないようです。例えば以下のコードは、かなり簡略化しているとはいえ普遍的なものですが、
type person = {name : string; title : string};;
type product = {name : string; price : int};;

let show_person (p : person) = Printf.printf "%s %s氏\n" p.title p.name;;
let show_product (b : product) = Printf.printf "%s:%d円\n" b.name b.price;;
 これでもなおエラーが発生してしまいます。どうやらperson.nameが後から登録されたproduct.nameによって上書きされてしまったらしく、4行目のnameへのアクセスがproductに対するものとみなされてしまい、型エラーが発生します。非常に不便なのですが、何とかならないのでしょうか。
 本当はこの時点で取り上げるべきものではありませんが、どうやらモジュールによってネームスペースを区切るしかないようです。なお、通常のモジュールはファイルごとに記述されるのが普通であり、以下のようなモジュールは「サブモジュール」と呼ばれるようです。
(* 個人データ用のモジュール *)
module Person_Module = struct
	type person = {name : string; title : string}
end

(* 書籍用のモジュール *)
module Book_Module = struct
	type book = {name : string; title : string}
end

(* 非常に手間がかかるモジュール名の記述 *)
let show_person (p : Person_Module.person) = Printf.printf "%s・%s氏\n"
	p.Person_Module.title p.Person_Module.name;;
let show_book (b : Book_Module.book) = Printf.printf "%s著 %s\n"
	b.Book_Module.name b.Book_Module.title;;

(* 使用例 *)
show_person {Person_Module.name = "イチロー";
	Person_Module.title = "とうしゅ"};;
show_book {Book_Module.name = "麻生太郎"; Book_Module.title = 
	"とてつもない日本 フロッピー1枚でコンピュータをつなぐ技術"};;
 これで無事に、イチロー氏のデータが個人データとして表示され、麻生氏の著書が書籍データとして表示されます。Cですら異なる構造体間の同じ名前は同一とはみなされないのに、より後進のはずのOCamlにおいてそれができないのは非常に不便ではありますが、基本的にincludeしさえすればすべて同一名前空間に置かれてしまうCと違い、OCamlではモジュールで機能と名前空間を細分かできますので、おそらく実用上は問題にならないのでしょう。
カテゴリ [開発魔法][社会問題][経済・知的財産] [トラックバック 1][コメント 0]

迷走郵政反対論
2009/02/15(Sun)14:12:47
 この期に及んでもなお迷走の麻生氏は、金配りについても「辞退する」「辞退するとは言っていない」などと発言をたびたび翻し、その上今度は郵政民営化にまでケチをつけるに至りました。最初から案として破綻しているのが明白であった金配りで迷走が発生するのは当然としても、この期に及んでの郵政発言は理解に苦しみます。
 しかも、その発言の内容もまた迷走しています。最初は「民営化には賛成ではなかった」と称しておきながら、自身の内閣が郵政選挙による2/3の議席を使用しており、しかも当時の麻生氏が小泉内閣の総務相ポストに就いていたことを指摘されると、今度は「賛成ではなかったが、勉強して賛成になった」と言い出しました。さらには「選挙で争われたのは郵政民営化の是非であって、4分社化などほとんどの国民は知らない」として、民営化に関しては必ずしも反対ではないものの、それに至る方法の見直しは行われてしかるべきとの認識を示しました。
 これに怒ったのが郵政民営化を掲げた小泉氏で、「怒るというよりあきれちゃう」と麻生氏の姿勢を批判、金配りについても再可決に難色を示しました。この発言のおかげでまた党内は大混乱を起こし、小泉氏に対しては「今さら言うべきことではない」との批判がある一方、支持する者も出ないとは言い切れないことから、麻生氏や金配りへの批判の強さもあり、造反阻止や火消しに躍起になっているようです。
 私は基本的に、小泉氏には良い印象を抱いていません。この内閣が仕掛けた低IQ選挙により、衆院選はほとんど人気投票のような状態になってしまい、一大争点の郵政に関してもほとんど議論が尽くされないまま、勢いだけで乗り切られてしまいました。さらに、それ以外の内政や外交、例えば格差問題や規制緩和、経済対策、社会福祉、イラクやアフガニスタンの問題といった重要課題は郵政以上におざなりになり、まともな議論もなされないままでした。さらに、露骨な人気取りとして堀江氏や料理研究家が擁立される始末で、料理研究家は小選挙区で落選しつつも当選保証の比例で復活、料理番組のために郵政採決を欠席する始末です。これのせいでまともな政策論争に基づく選挙は大幅に遠のき、政策論争と競争原理のある政治の時代はまたも先延ばしとなりました。こうして低IQ選挙は無残なまでの負の遺産を残し、政治の劣化を招いたのみで終わりました。あの選挙が日本のためになった点など何一つとして存在しません
 今回小泉氏がしゃしゃり出てきたことについても、小泉氏にはもはやそのような権限はないため、ただの「出るくい」でしかありません。くだらない茶番劇の再来を狙っているのであれば、それは単なる老害というものです。
 しかし、小泉氏の今回の意見はそれなりに妥当なものであり、完全に無視するのもよくありません。郵政発言は明らかに麻生氏の迷走ですので、小泉氏の方に分があります。また、金配りについても意地を張ってまで再可決するほどの政策ではなく、むしろ頭がどうかしているとしか考えられない、戦後最大級の愚策でしかありません。内閣からは「今さら言われても困る」との批判が出ているようですが、今までにも金配りはほとんど国民の支持を得られておらず、党内からも異論や批判が噴出し、加藤氏が「公明党と選挙協力しなければならないので賛成」とまで発言しているなど、国民からも党内からも反対意見が噴出していたことが分かります。それに一切耳を貸さずに状況を進め、既成事実ができあがりつつある時点での批判には「今さら言っても遅すぎる」と反論するのでは、一体いつ言えば聞く気があるのでしょうか。麻生氏サイドの言い分は、完全に論理が破綻しています。
 一部世論にも苦言を呈しておかなければなりません。麻生氏が小泉氏の政策を否定してみせるのには、「小泉改革からの脱却」をアピールする狙いがあるとも考えられます。小泉改革は格差を深刻化させ、郵政民営化にしてもサービス切り捨てが顕在化してきており、これを見直して世論の支持を獲得しようという魂胆です。確かに、小泉改革は「規制緩和こそ善」なる半ば狂信的なフレーズにより、必要な規制までもを徹底的に叩き潰し、今日の日本の状況を作り上げてしまいましたので、私は脱却自体には反対しません。
 しかし、問題は郵政の方です。郵政民営化をめぐっては、今になってようやく世論から不満が噴出するなどしており、麻生氏はこの層をつかみたいものとみられます。しかしながら、サービスの低下や過疎地切り捨ての問題は最初から分かっていたにもかかわらず、それでもなお小泉内閣の郵政民営化を支持し、小泉政権に票を投じたのは一体誰なのでしょうか。「当時は分からなかった」なる言い訳は通用しません。小泉氏の低IQ選挙に引っかかったのだとしても、あのような簡単なトリックは容易に見抜けるはずですので、引っかかる側にも責任があります。自民党以外を支持した人は、とばっちりを食ったようなものですが、国民の過半数の支持を得たのは自民党なのですから仕方ありません。狭義には小泉氏を支持・投票した者は、広義には小泉氏を支持した日本国民は、今さら「不便になった」からといって郵政民営化にケチをつける資格はないはずです。これでは、仮に麻生氏がこの層をつかんで浮上したとしても、また前回の二の舞になるだけでしょう。
 今回の「郵政」発言の問題に戻りますが、麻生氏は本当に郵政民営化を見直す気なのでしょうか。氏のこれまでの発言パターンからして、ほとんど何も考えずに本能のまま口にした可能性が高そうですが、仮に4分社化などといった方針を見直す気があるのなら、私はこれにまでは反対しません。当時とは状況も違いますし、民営化過程で得られた知見を基に軌道を修正するのは、決して悪いことではありません。
 ただ、実際に見直しを行う意思があるのであれば、行動は公約に掲げて解散してからにすべきでしょう。「4分社化の見直し」とは物は言いようで、例えば金融の影響力に全く配慮しない形で民営化が進行してしまうと、超巨大金融の問題などは全く解決されないままになってしまい、民営化の意味が非常に薄くなります。民営化の方法のいじり方次第では、かつての郵政あるいは郵政公社とほとんど変わらない状態にしたりもできるでしょう。このような懸念から、選挙なしに見直しを行うのはルール違反です。
 今回の問題では、すべて麻生氏より小泉氏の主張の方に分があります。郵政選挙の議席で郵政を見直し、再可決してまで金を配る道理など存在しないのです。金配りと郵政見直しを公約にして解散し、世論の支持を得て初めて、金配りと郵政見直しの道理が得られます。
 ところで、いずれ行われるであろう衆院選にも懸念はあります。何でも自民党は、東国原宮崎県知事と橋下大阪府知事に協力を依頼しているというのです。またしても人気者を使って低IQ層に票を貢がせようとの魂胆でしょうか。このような選挙が日本のためになることなど、何一つとしてありません。人気がある時は「アキバ発の総理大臣」や「脳内メーカー」などの手段で人気を取っておいて、人気がなくなってもこれとは、麻生氏はどこまでいっても麻生氏のようです。

 厚生年金保険料について経団連が面白いことを言い出したようですので、いつもながらここで彼らの欺瞞について分析しておきましょう。
 経団連曰く、まず消費税は2025年までに17%にするのだそうです。これによって国民年金を全額税化し、かつ医療にも回します。また、厚生年金保険料を15%から10%に引き下げるものの、現状では労使折半で7.5%となっている保険料率のうち、従業員の負担のみを5%軽減して2.5%とし、企業の負担は7.5%のままに据え置くそうです。法人税はいつもながら10%下げろと主張しています。
 年金全額税化と消費税については、税の上げ幅には異論もありそうですが、ここまではある程度妥当な主張です。しかし、ここで注目したいのは厚生年金保険料の減額です。経団連としては非常に珍しいことに、「企業の負担分は軽減せず、従業員の負担のみを5%軽減する」と言い出しているのです。このご時世に法人税減税を言い出せば批判されますので、それを回避するためのものとも見られているようです。
 しかし、この時点で見抜かれた方もいるかもしれませんが、仮に法人税減税の話がなかったとしても、実はこれ自体が詭弁なのです。いつでも自分の利益のためだけに行動している経団連が、理由もなくこのような案を言い出すわけがありませんので、上記案が実際に実行された場面を考えてみたところ、これ自体が企業にとって有利な計算となることが分かりました。
 まず最初に、上記案を単純に考えたとしても、企業が新たに負担しなければならない額はゼロです。消費税を上げる代わりに従業員の負担分を下げるのであって、企業負担分が増えるわけではありませんので、これだけなら企業にとって「あってもなくても同じ」案であるといえます。消費税増税による消費低迷といった間接的影響はあるかもしれませんが、保険料の負担といった観点ではダメージはありません。つまり、これは少なくとも企業にとって不利な案ではありません。
 それでは、なぜ同案が企業にとって有利といえるのでしょうか。所得税などは考えないものとして、あくまで単純計算の場合ですが、例を出して考えてみましょう。仮に20万円の給料の従業員がいるとして、ここからさらに保険料が引かれるとします。保険料は労使折半で7.5%ですので、従業員も企業も15000円の負担となります。したがって、この従業員の手取りは18万5000円となります。
 経団連の案が実施されると、20万円の10%である2万円が保険料となるものの、従業員の負担のみが2.5%に軽減され、企業負担は7.5%のままです。したがって、企業は15000円を負担し、従業員は5000円を負担して、19万5000円が残ります。これだけ見ると、確かに企業の負担額は変わっていません。
 しかし、良心的な企業はともかくとして、派遣使い捨てや偽装請負、無賃残業合法化なども辞さない企業連中が本当に従業員の負担を軽減しようとするでしょうか。現状では18万5000円の手取りとなる従業員が、経団連案では19万5000円となるなら、悪徳企業連中は差額分の給料を下げようとするのではないでしょうか。
 かくして、従業員の給料は1万257円カットされて18万9743円となり、従業員の保険料負担は2.5%の4743円で、減額後の所得は18万5000円となります。企業の保険料負担は7.5%の14230円となり、しかも給与を10257円カットしていますので、以前の15000円の負担に比べて11027円も得なのです。これなら経団連が主張したがるのも分かります。
 法人税減税については、あきれる以外にありません。検討するしないの次元ではなく、論外の一言です。経団連曰く、日本の法人税は欧州諸国よりも高いので、30%程度にして同等程度の税率にすべきとのことですが、日本の法人税には抜け道が多く、しかも欧州では直接的な法人税以外でも企業に応分の負担を求めており、日本の法人税率は決して高いとはいえません
 豊田市や企業城下町などでは、法人税の低迷で税収が極端に落ち込んで困り果てており、これを法人税の弱点として主張する声もあります。しかし、わざわざ説明するまでもありませんが、これは法人税を下げる理由にはなりません。それでも法人税を下げるべきというなら、利益比例の法人税を30%にする代わりに、大企業に対する外形標準課税を大幅に強化したり、従業員の福祉などを十二分に負担させるなどして、減税分を安定的な別の負担に組み替える策を同時に導入すべきです。欧州ではこれに近い制度になっているようですが、おそらく経団連は「法人税を欧州並みにしつつ、欧州でやっているような負担は一切しない」方針と見られますので、この案には反対してくる可能性が高いでしょう。
 また、法人税については「法人税が高いと企業が海外に流出し、日本は大打撃を受ける」なる論も見られますが、これはまじめな論客であれば絶対に行わない主張と言っても過言ではありません。主張されるにしても、他の対策や意見の付随となる程度がせいぜいでしょう。「日本の法人税は必ずしも高くない」ことを据え置いても、この論には2つの矛盾点が存在します。

1.実際の企業は、単に法人税のみを基準に国を選ぶのではなく、当該国の経済、国民、物価、地理、政情、流通、習慣などあらゆる要素を総合的に判断して選定するため、「法人税が下がると企業がいなくなる」というのは他の要素を無視しており、不当な主張である。
2.仮に上記の主張が事実であるとして、企業がより法人税の低い国に流出してしまうのであれば、日本は減税競争でタックスヘイブンに勝てるわけがないのだから、企業はすべてタックスヘイブンに流出してしまう。つまり、結局日本は流出を止められないことになる。

 つまり、企業の流出を根拠に法人税減税を主張するのであれば、法人税以外の面で日本の価値を高める方法を論じる方が建設的です。それでもなお「法人税が高いと企業は逃げる」というのなら、どうあがいてもタックスヘイブンにはかなわないのですから、「あきらめましょう」と主張するよりないのです。
 経団連や政治家の主張の中には、不当ながらも緻密かつ巧妙に作り上げられたものも存在しますが、主張をしっかり見て考えるようにすれば、矛盾は必ず露呈してきます。郵政にしてもそうですが、「小泉さんの姿勢が格好良く、郵政民営化がなされれば良い社会になりそうだからと賛成したら、近所の郵便局がなくなった。そんな話は聞いていない」と後から言ってみても何の役にも立ちません。経団連や社会保障国民会議といった連中の、もっともらしい主張にだまされないためには、まず妥当性を考え、相手の意図を推測し、欺瞞を見抜くようにすることが大切です。

 関数型言語を代表する命令・関数には、どのようなものが存在するでしょうか。car/cdrはLisp使いなら誰でも知っていますが、HaskellやOCaml、F#といったLisp以外の関数型使いの方にはなじみがなさそうです。パターンマッチやカリー化などは、必ずしも存在するとは限りません。末尾再帰もサポートされているとは限りません。
 そうなると、ここはmap関数などの関数渡し関数を代表にするしかないでしょう。大抵の関数型言語ではmapですが、Lispではmapcarです。関数のシグネチャは「(a -> a) -> [a] -> [a]」や「(a -> b) -> [a] -> [b]」となります。これと類似のfilter関数も有名で、こちらは「(a -> bool) -> [a] -> [a]」です。
 余談ながら、関数型から手続き型を習得しようとする人は「forやwhileは末尾再帰と同じ」「foreachはmapと似たようなもの」と覚えたりするそうです。そうなると、手続き型の代表選手はwhileやfor辺りでしょうか(某言語ではPERFORMですので、この言語だけしか使ったことがない人はforなど知らないかもしれません。しかし、この言語以外の言語を使えない人など、21世紀の現代に存在するのかは不明です)。
 Common Lispでは、関数に#'なる記号を付加して関数渡しを行っていました。また、関数と変数が区別されているため、渡された関数をそのまま呼び出す行為は許可されておらず、applyなりfuncallなりを使用して呼び出さなくてはなりません。一方、大抵の関数型言語では関数が完全にファーストクラス扱いになっており、関数をそのまま使用できます。
 それでは、Prologはいかなる仕様になっているのでしょうか。Prologでは述語を渡す際には特別な表記は必要ありませんが、呼び出す際には必要になっています。述語を呼び出すにはapply/2を使用し、2つ目の引数は述語に渡す引数のリストです。
% 1つ目の引数に述語名、2つ目に引数のリストを記述する
?- apply(reverse , [[1 , 2 , 3] , R]).
R = [3, 2, 1].

?- apply(> , [10 , 20]).
false.

% is も述語であるため、apply で使用できる
?- apply(is , [R , 10 + 20]).
R = 30.

% (...) も述語であるらしく、apply で使用できる
% (...) に引数があるわけもないので引数は取らない
?- apply((R is 10 + 20 , S is R + 40) , []).
R = 30,
S = 70.
 これを使えば、mapやfilterも実装可能です。
map(P , XS , R) :- in_map(P , XS , [] , R).
% apply によって述語呼び出し
% 述語の1つ目の引数にリストの要素を渡し、2つ目で結果を受け取る
in_map(P , [X | XS] , RS , R) :-
	apply(P , [X , D]) , ! , in_map(P , XS , [D | RS] , R).
in_map(_ , [] , RS , R) :- reverse(RS , R).

filter(P , XS , R) :- in_filter(P , XS , [] , R).
in_filter(P , [X | XS] , RS , R) :-
	apply(P , [X]) , ! , in_filter(P , XS , [X | RS] , R).
in_filter(P , [_ | XS] , RS , R) :- ! , in_filter(P , XS , RS , R).
in_filter(_ , [] , RS , R) :- reverse(RS , R).

% テスト用の述語

% 2乗述語
pow(D , R) :- R is D * D.
% 100以下
under_100(A) :- A < 100.
 使い方は関数型のmapと同じです。ただし、Prologでは複数の値を返す場合がありますが、上記mapで受け取る値は最初に得られたものとなります。また、Prologでは引数に結果を格納して受け取るスタイルを取りますので、mapやfilterは必然的に3つの引数となります。
% map を使用
?- map(pow , [1 , 2 , 3 , 4 , 5] , R).
R = [1, 4, 9, 16, 25].

% filter を使用
?- filter(under_100 , [120 , 60 , 96 , 102 , 50 , 113 , 201 , 5] , R).
R = [60, 96, 50, 5].
 次に、functorなる怪しげなものについて。Haskellでもそのようなものが出てきましたが、Prologでは「構造体のようなもの」とのこと。次のように使用できます。
get_first_name(person(First , _) , R) :- R = First.
 呼び出しはこうなります。
?- get_first_name(person('Barack' , 'Obama') , R).
R = 'Barack'.
 述語ではなくファンクタらしいのですが、記法は全く同じです。確かにHaskellのdataの展開に似ていないことはありません。
 名前を表示するプログラムを書くなら、以下のようになるでしょう。
show_person(person(F , L)) :- write(F) , write(' ') , write(L) , nl.
 これを踏まえて考えてみましょう。次のようなリストがあるとして、
% lang(Name , Birth_Year)
[lang('Perl' , 1987) , lang('Ada' , 1979) , lang('Lisp' , 1960) , 
lang('Java' , 1995) , lang('COBOL' , 1960) , lang('C' , 1972)]
newest([lang('Perl' , 1987) , lang('Ada' , 1979) , lang('Lisp' , 1960) , lang('Java' , 1995) , lang('COBOL' , 1960) , lang('C' , 1972)] , R).  このリストで最も新しい言語の名前を取得したいとします(複数ある場合は最初のものを取得)。そのような述語は作成できるのでしょうか。
newest([X | XS] , R) :- in_newest(XS , X , R).
in_newest([lang(Name , Year) | XS] , lang(_ , NYear) , R) :-
	Year > NYear , ! , in_newest(XS , lang(Name , Year) , R).
in_newest([_ | XS] , N , R) :- in_newest(XS , N , R).
in_newest([] , lang(Name , _) , Name).
 見た目が長いだけで、やっていることは簡単です。
?- newest([lang('Perl' , 1987) , lang('Ada' , 1979) , 
	lang('Lisp' , 1960) , lang('Java' , 1995) , 
	lang('COBOL' , 1960) , lang('C' , 1972)] , R).
R = 'Java'.
 それでは、「指定した年以降」の言語を取得したい場合には、どのような述語になるのでしょうか(複数の結果が該当する場合には、Prologらしくセミコロン入力で次候補を捜索・表示できるようにする)。
after([lang(Name , Year) | XS] , Y , R) :-
	(Y =< Year , R = Name) ; after(XS , Y , R).
 あるいは、このように書いても構いません。
after([lang(Name , Year) | _] , Y , Name) :- Y =< Year.
after([_ | XS] , Y , R) :- after(XS , Y , R).
 こちらは先の例よりも簡単でしょう。
?- after([lang('Perl' , 1987) , lang('Ada' , 1979) ,
	lang('Lisp' , 1960) , lang('Java' , 1995) ,
	lang('COBOL' , 1960) , lang('C' , 1972)] , 1962 , R).
R = 'Perl' ;
R = 'Ada' ;
R = 'Java' ;
R = 'C' ;
false.
 ファンクタといえば、リストもこれの一種とされており、「.」ファンクタなのだそうです。おそらく[...]は糖衣構文なのでしょう。
?- R = .(1 , .(2 , .(3 , []))).
R = [1, 2, 3].
 何ともLispのようなリストです。ちなみに、Lisp同様に「リストの構造から外れる」ものも一応作成できるようです。
?- R = .(.('one' , 'eins') , .(.('two' , 'zwei') , .(.('three' , 'drei') , []))).
R = [[one|eins], [two|zwei], [three|drei]].
 このようなリストは「|」で区切って表現するようです。なるほど、これでPrologが[X | XS]のようにして残りのリストをXSに格納できる理由が分かりました。
 以下の単純な例で考えてみます。
.(1 , .(2 , .(3 , []))).
% [1 , 2 , 3] と等価
 仮にこれを[X | XS]で取り出すと、X = 1 , XS = [2 , 3]となるでしょう。なぜなら、「|」は「.」ファンクタ構造の1つ目と2つ目を区切る糖衣構文であるためです。つまり、
[X | XS]
.(1 , .(2 , .(3 , []))).
 このようにカンマの前後に対応し、したがって
X = 1
XS = .(2 , .(3 , [])) = [2 , 3]
 こうなります。ここで「|」が絡んでくるとは、何とも奥深いです。

 それから、比較演算子について少々。
 Prologには2種類の比較演算子があります。「==」と「=:=」です。否定にも2種類あり、それぞれ「\==」と「=\=」が先の演算子の否定に相当します。しかし、このような使い分けは他の言語ではほとんど見られません。一体何が違うのでしょうか。
 まず、単純に数値なりを比較するだけであれば、両者とも結果は同じです。
?- 1 == 1.
true.

?- 1 =:= 1.
true.

?- 1 \== 1.
false.

?- 1 =\= 1.
false.
 それでは何が違うのでしょうか。
 簡単に言えば、通常の「==」あるいは「\==」が単に両者が等価であるかどうかを表すものであるのに比べ、「=:=」や「=\=」の場合には、両辺をisで演算し、その結果同士を比較した場合とおおむね同等の結果が得られるようになっています。このような理由により、述語を「=:=」や「=\=」で比較しようとすると上手くいきませんので、「==」や「\==」で比較する必要があります。
 一方、「=:=」や「=\=」による比較では演算が行われますので、比較対象の片方または両方を式としたい場合には便利です。
% 2+3 と 2+3 は等価なので true
?- 2+3 == 2+3.
true.

% 5 と 5 は等価なので true
?- 2+3 =:= 2+3.
true.

% 2+3 と 3+2 は等価ではないので false
?- 2+3 == 3+2.
false.

% 5 と 5 は等価なので true
?- 2+3 =:= 3+2.
true.
 右辺値と左辺値をそれぞれisした後、これを比較するのとおおむね同じなのですから、次の述語は「=:=」の動作と似ています。
eq(A , B) :- IA is A , IB is B , IA == IB.
 ただし、次のような場合には差異が生じてしまいます。
?- 1 =:= 1.0.
true.

?- eq(1 , 1.0).
false.
 「=:=」ではこれでもtrueになるようです。ちなみに、eq/2の場合はfalseでしたが、単純に「==」で比較しても同じくfalseです。「=:=」が型を無視して値のみを評価しているのか、型を小数に統一してから比較しているのかは不明ですが、確かに「9 / 4 - 0.25 =:= 2」がfalseであるとすれば理不尽極まりない(左辺値の9 / 4 - 0.25は2.0であるため、この式は2.0と2の比較となる)ので、このような場合のための配慮でしょうか。
 なお、「>」や「=<」などの不等号でも両者に対して演算を行ってくれます。
?- 2+3 > 4.
true.
 それ以外にも「\=」なる演算子が存在します。これは「=」の否定版なのですが、それでは指定した値以外全部が代入されるのでしょうか。無論、そのようなことは不可能ですが、さすがに「=」の否定形だけはあり、意味としては「片方の値がもう片方の値たり得ない場合はtrue」となっているようです。大抵の場合は「\==」と同じ結果を返しますが、以下のような場合に差が生じます。
?- R \== 1.
true.

?- R \= 1.
false.
 この時点ではRの値は確定していません。したがってRは1ではなく、「R \== 1」はtrueを返します。しかし、Rの値が1に確定する可能性もあるため、「R \= 1」ではfalseになるものと考えられます。
 「\=」の仕様は、次のようなコードでより明確となります。
?- a(R) \= a(1).
false.

?- [1 , R , 3] \= [1 , 2 , 3].
false.

?- a(R) \= b(1).
true.

338 ?- [1 , R , 3] \= [1 , 2 , 4].
true.
 最初の2つの場合、a(R)はa(1)に、[1 , R , 3]は[1 , 2 , 3]になる可能性があります。したがってfalseを返します。一方、後の2つはどうがんばっても等価にはなりようがありません。したがってtrueです。

 これでPrologの主要な機能はほとんど網羅したはずです。言語自体はそれほど難しいものではなく、C++やAdaに比べれば非常に楽なものなのですが、主に苦労させられるのは論理型の概念に関する部分でしょう。Prologから論理型のエッセンスを抜き去り、より実用を容易にしたのが関数型言語Erlangなのですが、Prologも使い方次第で大抵の処理ができるのは、ここまでのコードを見ての通りです。
 以下、Prologを使って色々な処理を作成してみます。

クイックソート
 手続き型・関数型を問わず、ソートは非常に基本的な処理です。要求される場面も多く、これが実装できないような言語があっては困ります。
 Prologには最初からsort/2が用意されていますが、ここでは自分でクイックソートを作成してみましょう。
q_filter(A , XS , RL , RR) :-
	in_q_filter(A , XS , [] , [] , RL , RR).
in_q_filter(A , [X | XS] , LS , RS , RL , RR) :-
	A < X ,
	(! , in_q_filter(A , XS , [X | LS] , RS , RL , RR)) ;
	(! , in_q_filter(A , XS , LS , [X | RS] , RL , RR)).
in_q_filter(_ , [] , LS , RS , LS , RS).

quick_sort([X | XS] , R) :-
	q_filter(X , XS , LS , RS) ,
	quick_sort(LS , QLS) , quick_sort(RS , QRS) ,
	append(QLS , [X | QRS] , R).
quick_sort([] , []).
 concatはリストの連結、q_filterは特定の数値より小さいものとそうでないものをそれぞれ別のリストに格納する処理です。
 使い方は実にそのままです。
?- quick_sort([120 , 60 , 96 , 102 , 50 , 113 , 201 , 5] , R).
R = [201, 120, 113, 102, 96, 60, 50, 5].
 無事にソートがなされているようです。

とある問題A
 このような言語を見ると、解いてみたくなるのが「とある問題」です。

問題
 ここに開発者が3人います。ところが、何とそのうち2人は開発の「か」の字すら知らないというのです。開発者は嘘をつきませんが、開発者でない者は発覚を回避するため必ず嘘をつきます。果たして嘘つきは誰でしょうか。
 以下、この3人の言い分です。

開発者サーラ「私が嘘を?身に覚えがございませんが・・・。そういえば、イリアスさんが嘘をついているとのことです」
開発者イリアス「私が開発者じゃない?失礼ね。バカなこといわないでよ。ただでさえ、Unicodeのデリゲートの扱いに困ってるのに。え?デリゲートじゃなくてサロゲートだ?単なる言い間違いよ!私は嘘なんかついてないわ!」
開発者シェリー「フロッピー1枚でコンピュータ同士をつなぎ、書類をすべて廃するのが未来の構想ですね。えっ、フロッピーはもはや時代遅れの産物で、しかもコンピュータ同士はつなげない?いや、その、あの・・・と、とにかく、私は嘘つきではありません。開発者でないとすれば、サーラさんです」

模範解答
 Prologでの求め方は色々存在しますので、以下は一例です。
check(truth).
check(lie).

val(truth , 1).
val(lie , 0).

q(Sara , Ilias , Shelley) :- check(Sara) , check(Ilias) , check(Shelley) ,
	Sara \= Ilias , Shelley \= Sara.
 これを実行すると、
?- q(Sara , Ilias , Shelley).
Sara = truth,
Ilias = lie,
Shelley = lie ;
Sara = lie,
Ilias = truth,
Shelley = truth ;
false.
 嘘つきは2人ですので、サーラのみ正直となります。全部Prologに任せたければ、
check(truth).
check(lie).

val(truth , 1).
val(lie , 0).

values([X | XS] , V , R) :- val(X , D) , N is V + D , values(XS , N , R).
values([] , R , R).

q(Sara , Ilias , Shelley) :- check(Sara) , check(Ilias) , check(Shelley) ,
	Sara \= Ilias , Shelley \= Sara ,
	values([Sara , Ilias , Shelley] , 0 , 1).
 などと書けるでしょう。

とある問題B
 これもまた「どこかで見たような問題」です。

問題
 p , qを異なる自然数とする。このとき、与えられた自然数dについて、d以下の自然数kのうちで
k = mp + nq (m, nは0以上の整数)
 のように表すことができるものを小さい順にすべて列挙し、最後にその個数を表示したい。そのような述語を作成しなさい。

模範解答
 これをPrologでも解いてみる理由は、何といっても問題への皮肉私の茶目っ気です。当然ではありますが、解ける問題です。
 Prologらしく書けば、こうなります。
answer(D , P , Q , R) :- k_loop(D , 1 , P , Q , R).
k_loop(D , K , P , Q , R) :- D >= K , k_check(K , P , Q , R).
k_loop(D , K , P , Q , R) :- D >= K , ! ,
	NK is K + 1 , k_loop(D , NK , P , Q , R).
k_loop(D , K , _ , _ , R , R) :- D < K.
k_check(K , P , _ , R) :-
	I is K mod P , I == 0 , ! , match(K , R).
k_check(K , P , Q , R) :- p_loop(K , P , 0 , K , Q , R).
p_loop(K , _ , VP , MP , Q , R) :- VP =< MP ,
	I is K - VP , J is I mod Q , J == 0 , ! , match(K , R).
p_loop(K , P , VP , MP , Q , R) :- VP =< MP , ! ,
	NP is VP + P , p_loop(K , P , NP , MP , Q , R).
match(K , K).
 Prologらしいコードだけに、セミコロンで次の候補を表示できます。
answer(15 , 3 , 7 , R).
R = 3 ;
R = 6 ;
R = 7 ;
R = 9 ;
R = 10 ;
R = 12 ;
R = 13 ;
R = 14 ;
R = 15 ;
false.
 ただし、問題は「小さい順にすべて列挙し、最後にその個数を表示」とのことですので、個数が欲しいところです。個数を変数として取得したければ、このようになるでしょう。
answer(D , P , Q , R) :- k_loop(D , 1 , P , Q , 0 , R).
k_loop(D , K , P , Q , NR , FR) :- D >= K ,
	(k_check(K , P , Q , NR , R) ; R = NR) ,
	! , NK is K + 1 , k_loop(D , NK , P , Q , R , FR).
k_loop(D , K , _ , _ , R , R) :- D < K.
k_check(K , P , _ , NR , FR) :-
	I is K mod P , I == 0 , ! , match(K , NR , FR).
k_check(K , P , Q , NR , FR) :- p_loop(K , P , 0 , K , Q , NR , FR).
p_loop(K , _ , VP , MP , Q , NR , FR) :- VP =< MP ,
	I is K - VP , J is I mod Q , J == 0 , ! , match(K , NR , FR).
p_loop(K , P , VP , MP , Q , NR , FR) :- VP =< MP , ! ,
	NP is VP + P , p_loop(K , P , NP , MP , Q , NR , FR).
match(K , R , NR) :- write(K) , nl , NR is R + 1.
 あるいは、前者の模範解答に対してfindall/3なる述語を適用する方法もあります。この述語には、「全候補をリストにして返す」効果があります。
?- findall(X , answer(15 , 7 , 3 , X) , R).
R = [3, 6, 7, 9, 10, 12, 13, 14, 15].
 リストですから長さを求めるのも容易でしょう。
 それにしても、このfindall/3は一体どのように実装されているのでしょうか。正攻法では実装できない気がしてなりません。

その他の機能
 四則演算その他以外にも、sinやatanなどの三角関数も用意されています。これは値を返すものであるため、述語ではなく関数です。ちなみに足し算なども関数です。計算結果はisによって取得します。
?- R is sin(0.707).
R = 0.649556.

?- R is atan(0.707 , 1).
R = 0.615409.
 このようなものを見ると、自分も関数を作りたくなってしまうのが人情です。せっかくsinやatanがあるのですから、それにあわせてradとdegでも作ってみましょうか。radはラジアンに変換するもので、degはラジアンを360度単位の角度に戻すものです。Prologには幸いにもpi関数があるようですので、実装は以下のようになります。
rad(A , B) :- B is A * pi / 180.
:- arithmetic_function(rad/1).
deg(A , B) :- B is A * 180 / pi.
:- arithmetic_function(deg/1).
 まず述語の記法で作成し、arithmetic_function(関数名/引数の個数)でそれを関数とします。使用方法はビルトイン関数と同じです。
?- R is deg(acos(sqrt(2) / 2)).
R = 45.0.
 ただ、この関数化の動作はどうも好まれていないような印象を受けます。Prologは基本的に述語を使用するプログラミングスタイルですので、arithmethc_functionはあくまでもっぱら計算に使用するユーティリティ関数を作成するのに用いた方がよさそうです。

 Prologには他にも色々な機能が用意されていますが、ここですべて取り上げるのは当然ながら不可能ですので、いずれ他の機能も使ってみるものとして、今回はひとまずここまでとします。ご興味を持たれた方には、INRIA(フランス国立の研究所でOCamlの開発元。技術というものが絶望的なほど分かっていない某国にも見習って欲しいものです)のサイト中のProlog: The ISO Standard Documents、あるいは英語版WikipediaのPrologの項及びその外部リンク、Wikibooksのビルトイン述語を列挙したページSWI-Prologのドキュメントなどをおすすめしておきましょう。
カテゴリ [開発魔法][社会問題][経済・知的財産] [トラックバック 0][コメント 0]

ゼロ相続税政策
2009/02/10(Tue)00:07:19
 金配りに始まって、派遣村の人々を(機動隊とも衝突した暴動である)学生運動呼ばわりしたり、渡りあっせんを認めると言ったり禁止と言ったり、国会を迂回して金配りをしようとしてみたり、郵政民営化に反対と言った後で賛成してみたりと、このところ麻生氏及び内閣・自民党の面々には意味不明な行動が目立ちます。サブプライムからの一連の不況の波をかぶっていながら内輪モメとは、どうやら麻生内閣には相当の余裕があるようです。
 ところが、これらだけでも憂慮すべき事態であるにもかかわらず、このほどさらにとんでもない案が登場しました。無利子である代わりに相続税を免除する国債を作ろうという案が自民党内に持ち上がってきたのです。最近は「政府紙幣」などいくつかの奇策が論じられているようですが、これもその1つであるといえそうです。
 経営学上、現在の資産は未来の同額の資産よりも価値が高いとみなされるため、この案の理念は分からないわけではありません。無利子で手に入れた金を運用にまわせば収益が期待できますし、これで有利子負債を圧縮すれば利払いが回避できます。国としては、相続税を得ない代わりに運用益や有利子負債の削減で利益を得られれば良いわけですし、利子を気にする必要のない借金ができるのは確かに魅力でしょう。十分な運用益の確保なり負債の圧縮なりが可能になれば、相続税を得るよりも利益が大きくなるとの考えがあったとしても不思議はありません。
 しかしながら、私は現段階ではこの制度に強く反対します。この案はあくまで「案」の段階に過ぎませんので、相続税は全額免除か一部免除か、国債にはどのような制限をつけるのかなど、まだはっきりとしたことは分かりませんが、いずれにしても得策とは考えられません。
 この問題に入る前に、まずは相続税法について簡単に解説しておきましょう。相続税とはその名の通り、所有者の死去などによって他者に受け継がれた財産にかかる税で、税率は相続額によって変わります。また、同法15条によって定められている基礎控除の規定によれば、色々と複雑な規定はあるものの、ごく単純には「5000万円 + 相続人の数x1000万円」以内であれば相続税はかかりません。したがって、一般人には相続税がかからないか、かかったとしてもごく一部にしかなりませんので、対象の多くは資産家であるといえます。
 同法16条によると、税率は以下のように定められています。

千万円以下の金額百分の十
千万円を超え三千万円以下の金額百分の十五
三千万円を超え五千万円以下の金額百分の二十
五千万円を超え一億円以下の金額百分の三十
一億円を超え三億円以下の金額百分の四十
三億円を超える金額百分の五十

※2009/02/13追記
 この表だけでは分かりづらいので、上記リンクの「相続税法」をわざわざ読みたくない方のために、計算方法を補足しておきます。
 まず相続額から控除額を減算します。計算式は上記の通り、単純には「5000万円 + 相続人の数x1000万円」です。こうして得られた相続税対象額のうち、まず1000万円までの部分に10%の税をかけ、それ以上で3000万円までの部分には15%の税をかけます。これを繰り返し、全部合計した額が課税額となります。
 仮に相続額10億、控除6000万円であるなら、9億4000万円は3億円を超えていますので、3億円以下の部分をすべて適用し、まず1億300万円が課税されます。さらに、3億円以上の部分が6億4000万円ありますので、こちらには3億2000万円が課税されます。したがって、最終的には4億2300万円(約42%)の課税です。同じく控除額を6000万円として、相続額が20億円なら9億2300万円(約46%)、1億円なら600万円(約6%)と、税率は金額に強く比例します。

 つまり、相続税は累進性の高い税であるといえます。
 無利子国債の話に戻りましょう。今回問題の無利子国債とは、すなわち上記の税を免除するものです。免除が全部か一部かは不明ですが、前述の通り相続税は一般人にはあまり縁のないものですので、この案は金持ちの選択肢を増やすものでしかあり得ません。対象者は当然、自分にとって最も有利なもの、すなわち相続税の累進性ができるだけ回避され、しかも国に支払う額が最小限となる方法を選ぶでしょう。したがって、この案に賛成できる理由が見つかりません。
 例外として、もし以下のような極端な条件で国債を発行するのであれば、支持しても良いかと考えています。いずれの方法も、生前贈与で相続税を回避したり、国債が市場で売買されたり、経済状況が変化したりといった可能性を考えると、必ずしも思惑通りに進むとは言いがたいのですが、少なくともこの程度の条件はつけなければ、到底容認できるものではありません。

・超長期無利子国債
 無利子国債の相続税を免除する代わりに、例えば30年後に同額が得られる国債を発行する方法です。課税対象額が3億円以上の部分ともなると、単純計算では実にその半分が相続税として徴収されるのですが、多少のことでこれを免除してしまっては、課税逃れに使われるのは確実でしょう。何といっても半額、つまり最大課税対象額が10億なら5億が徴収されるのですから、金持ちがこれを回避したがるのは当たり前です。
 定められた期間内に、手元にある資金を倍にするのに必要な年利(複利計算)は、30年であれば約2.337%、50年なら約1.396%といったところです。仮に最大課税対象額が10億あるとして、そのまま相続すれば半分が消えて5億となります。これを元の額に回復するには、前述の年数と金利が必要なのです。もしそれを普通に相続した後、1.396%以上の年利を出せないのであれば、50年無利子国債の方が得ということになります。
 現在の経済状況では1%の金利すら困難ですが、30年や50年となれば状況も大幅に変わると考えられますし、物価が上がれば国債の価値は相対的に低くなります。経済成長などを加味すれば、数%程度の金利は妥当なところでしょう。もし相続税を免除した上、国債購入額と同等の額面での返還を保証するのであれば、少なくともこの程度の長期国債にはならざるを得ません。国としては、無利子国債で有利子負債を圧縮すれば、その分の利払いの必要がなくなりますので、ここまで長期にしなくても十分に利益を得ることはできるはずですが、国は良くても制度としては不公平です。一般人が1%や2%程度の金利さえ容易には得られない中、金持ちが無利子国債を5年ほど保有して半額の相続税を回避したなら、利息に換算すると実に年利14.87%にもなるのです。これが不公平でないとすれば、何が不公平なのでしょうか。
 ただし、この国債を市場で自由に売買できてしまうと、相続前に買って終わったら売るなどして課税逃れが出ないとも限りませんので、この点の調整は必要です。

・高額打歩国債
 相続税による課税は、最大の課税対象部分では半額にも及びます。したがって、この徴収を逃れるためであれば、打歩(額面よりも売価の方が高い債権)発行の国債でも買う理由が存在します。仮に@160なるとてつもない額(160円払うと100円分の国債が得られる)の国債であっても、通常の相続税の最大課税であれば割合は半額となり、仮に課税対象額が16億なら8億が引かれてしまうのですから、この打歩国債を買って16億を10億にした方がまだ利益が大きいのです。
 実際には、相続税には5000万円以上の控除があり、課税対象額が3億円以上でなければ税の割合は半額より低く、また国債を保有している間は利子がつきませんので、それを考えると必ず得するとはいえないのですが、これほどであってもなお利益を得られるケースも存在するのです。

 上記の案はいずれも現実的なものとはいえず、私もこれらのような案が本気で実現されるなどとは考えていませんが、上記のようなあまりにも極端な案ですら、あくまで理論上の話ではありますが、金持ちに利益をもたらす場合があるのです。政府が検討するような案となればなおさらでしょう。そもそも、自分の利益にならない国債など誰も買うわけがないのですから、金持ちがこのような無利子国債をあえて購入するということは、彼らにとっては相続税上の利点がある、すなわち金持ちを相続税の面で優遇していることを意味します。
 「貧困の連鎖」さえ指摘されるこのご時世にあって、金持ちの相続税を優遇する国債を発行しようとは、一体どのような了見なのでしょうか。相続税以外の金持ち優遇については、例えば所得税の最高税率が引き下げられた事例について、金持ちの一部には「努力や才能の結果なのだから、金持ち優遇の批判は当たらない」と称する者も見られますが、私は当然ながらこのような見解には同意しません。しかし、仮に百歩譲ってその言い分を認めるとしても、相続税となればわけが違います。相続税まで優遇する必要がどこにあるのでしょうか
 確かに、国にとっては都合の良い案なのかもしれません。金持ちは国債によって相続税を回避し、国は無利子で得た借入金を運用したり、あるいは有利子負債の圧縮に使用すれば、双方が通常より多くの利益を得られる可能性があります。しかしながら、これを「ウイン・ウインの関係」などとして容認するのは早計に過ぎます。このような行為は、累進性、富の再配分、格差の固定化防止といった相続税の理念を否定するものであることを忘れてはいけません
 いい加減、政治家の誰かが相続をしたり受けたりするために言い出したのではないかと勘繰りたくもなります。案の内容が全くまとまっていない現段階では、あまり詳細な部分に立ち入って論じることはできませんし、金持ち優遇色が十分に薄められる可能性もゼロとまでは言い切れませんが、少なくとも現段階では本案には賛成しかねます。

 あるタレントのブログに対し、「女子高生コンクリート詰め殺人」の犯人であるなどと虚偽の書き込みをしていた19人が、名誉毀損や脅迫の容疑で検察官送致の運びとなりました。かの事件の痛ましさは今さら説明するまでもありませんが、名誉毀損や脅迫を受けた被害者は本事件とは何の関係もなく、したがって攻撃を受けるいわれは全くありませんでした。
 私はインターネットの問題をむやみに取り締まるのには反対の立場です。例えばWinny作者の逮捕のような問題に関しては、私自身はWinnyのようなソフトを使用した経験はありませんが、許されないと主張してきました。インターネットの規制法に関しても、基本的には反対の立場です。インターネットは基本的に自由であるべきで、規制法などによって下手に権力が介在しようものなら、なし崩し的に権力の介入が認められかねませんので、私は絶対にそれを容認しません。
 しかしながら、私は本件の警察の対応には強く賛同します。根も葉もない嘘によって他人の名誉を傷つけ、さらには脅迫を行った者までいたとなれば、それは単なる卑劣な犯罪行為に過ぎず、自由として守られる価値などありません。インターネットに無用な規制を持ち込んではいけませんが、こうした例は積極的に取り締まるようにすべきです。
 まずはっきりさせておかなくてはならないのは、もし本件の被害者が本当にコンクリート詰め事件の加害者であったとしても、中傷行為を行うのは許されないという点です。本件の被害者は言うまでもなく事件の加害者ではなく、中傷は完全に事実無根です。しかし、仮に事件の加害者に対してであっても、名誉を傷つけるような行為は許されません。
 以下、名誉毀損及び侮辱罪の引用です。

(名誉毀損)
第二百三十条  公然と事実を摘示し、人の名誉を毀損した者は、その事実の有無にかかわらず、三年以下の懲役若しくは禁錮又は五十万円以下の罰金に処する。
2  死者の名誉を毀損した者は、虚偽の事実を摘示することによってした場合でなければ、罰しない。

(公共の利害に関する場合の特例)
第二百三十条の二  前条第一項の行為が公共の利害に関する事実に係り、かつ、その目的が専ら公益を図ることにあったと認める場合には、事実の真否を判断し、真実であることの証明があったときは、これを罰しない。
2  前項の規定の適用については、公訴が提起されるに至っていない人の犯罪行為に関する事実は、公共の利害に関する事実とみなす。
3  前条第一項の行為が公務員又は公選による公務員の候補者に関する事実に係る場合には、事実の真否を判断し、真実であることの証明があったときは、これを罰しない。

(侮辱)
第二百三十一条  事実を摘示しなくても、公然と人を侮辱した者は、拘留又は科料に処する。
(太字は引用者による)

 名誉毀損罪には例外が2つあり、「死者の名誉を真実によって毀損した場合」と「事実の摘示が公共の利益のために行われ、しかも摘示された事実を真実と信じるに足る証拠があった場合」には罰せられません。逆に言えば、存命中の元犯罪者の名誉を「真実によって」毀損した場合であっても罰せられます。仮にコンクリート事件の加害者がブログを書いていたとしても、それを指摘して名誉を毀損すれば名誉毀損に当たります。ちなみに、名誉毀損の例外として「公共の利益にかかる場合」があると述べましたが、いくら相手が元加害者であったとしても、単にブログを書いているだけの相手の名誉を毀損する行為が「公共の利益」とみなされる可能性はほぼ確実にゼロです。すなわち、それは3年以下の懲役に処せられる恐れもある犯罪行為です。
 また、ブログなどを中心に、まれに「炎上」と呼ばれる現象が発生する場合があります。炎上の原因には様々なものがあり、万引きなどの犯罪行為をブログに書いたり、記事で他人を根拠もなく中傷したりといった行為によって炎上する非常識なケースもあれば、ブログの著者が意見の分かれる問題に言及したために、著者の意見に賛同しない人が寄ってたかってブログに書き込みを開始し、炎上してしまうケースもあります。
 「炎上」の書き込みの中にも、著者にしっかり反論または抗議するような比較的まともなものもあれば、単なる誹謗中傷や脅迫でしかないものもありますが、後者はほぼ確実に名誉毀損、侮辱、脅迫、威力業務妨害といった罪にあたります。また前者であっても、数百数千という大量の炎上書き込みが殺到している異常事態の中で、さらに書き込みを増やすわけですから、確実に罪に問われないとは言い切れません。短期間の大量書き込みによる負荷と容量の増大など、サーバー業者に実害を生じさせる可能性も考えられます。
 炎上の中には、明らかに著者に問題があるものも多く存在しますが、それでも誹謗中傷の免罪符とはなりません。著者が記事中で他者の名誉を傷つけ、その記事が炎上を起こしたとして、ここで著者を中傷するようなコメントが書かれていたら、両者ともに名誉毀損や侮辱罪に抵触しています。行動のレベルは全く同じと言い換えても良いでしょう。
 ましてや、議論のある問題について意見を述べたことに対する炎上や、今回のように事実無根の流言に端を発した炎上は、全く正当化のしようがありません。議論のある問題に対する炎上の場合、ブログ著者が多少「トゲのある」表現をして問題提起をしているケースも見受けられますが、このような問題提起の手法はかねてから様々なメディアで用いられており、特別なものではありません。これを行ってはいけないというのであれば、当たり障りのない表現しかできなくなってしまいます。
 既存のマスコミは様々な捏造や隠蔽を行っており、ことに議論のある問題、タブーとされる問題については、ろくに取り上げようとしないか、あるいは自社の主張に沿う情報ばかり取り上げる傾向にありました。インターネットが登場したおかげで、このようなマスコミの捏造・隠蔽報道を介さずに意見を述べたり議論をしたりできるようになったにもかかわらず、議論のある問題を書くと炎上の危険にさらされるようでは、インターネットで自由な意見を述べられなくなります。これは非常に大きな損失でしょう。
 さらに、ある大手サイトに「ブログ炎上」への対策を書いた記事がありましたが、ここでは「炎上させた側に責任がある」と言わんばかりの論調を取っており、「まず謝罪」が対策であるとしていました。確かに、炎上の中には著者に責任があるケースも多く、このような著者が謝罪を書くのは当然です。しかし、著者に責められる点があるからといって、中傷コメントで炎上に加担して良いわけがありません。本人は正義の味方にでもなったつもりなのかもしれませんが、これは単なる犯罪行為に過ぎず、その実KKKが黒人や黄色人種を誘拐してリンチを加えるのと何ら変わるところはありません。これは、今回の事件のように相手が何らかの事件の元加害者と噂されている場合も同じですし、仮にそれが事実であっても同じです。無論、相手にはこれといった非がないのに、意見が気に入らないからといって炎上に加担するような行為は、ただの一方的な言葉の暴力に過ぎません。
 時として、炎上なのか大量のまっとうなコメントなのか、意見なのか名誉毀損なのかが分かりづらいようなケースも存在するとはみられますが、少なくとも明らかに中傷にしか見えないような書き込みは、しっかり取り締まられなくてはなりません。
 違法行為はインターネット内だけにとどまるとは限りません。以前にイラク人質事件が発生した際、家族に大量の中傷電話がかけられたことがありましたが、社会生活を脅かしている分「炎上」より悪質といえます。Webサイトなどで被害者らの行為を「軽率」と批判するのは自由ですし、それが警察によって取り締まられるようなら私はそれを絶対に容認しませんが、電話をかけて中傷するなどといった行為が許されるわけがありません。名誉毀損や侮辱の他、精神的に追い詰めれば傷害罪すら適用される場合があります。ブログ炎上のコメントの中にはまっとうな意見もありますので、これを全部取り締まるのには賛成できませんが、被害者宅にわざわざ電話をかけたとなると、何をもってしても正当化は不可能ですから、このようなケースでは確実に全員が取り締まられなければなりません。こうした中傷をはじめとした、法を守る気も何もない嫌がらせに対しては、必罰による一般予防で当たる以外にありません。
 インターネットつながりで、最近の判決にも触れておきましょう。このほど、虚偽の書き込みに対する裁判の2審判決が出たとの記事がありました。1審では「インターネットの書き込みは信頼性が低いとみなされており、通常の報道ほど厳密な証拠は不要」との判決が出ていましたが、2審ではこれを破棄して「インターネットのみをそのような扱いにするのは不合理」との判決を下しました。これはおおむね妥当な判決でしょう。インターネットについて「便所の落書き」などと称する人も見られますが、司法は「便所の落書きではいけない」と言っているのです。インターネットは現在よりもさらに「信頼性のあるメディア」とみなされるようになるべきであり、「表現の責任」の自覚が必要です。
 例えば、先に取り上げたイラク人質事件ですが、当初に一部では自作自演説もささやかれたそうです。それを誇らしげに書いてあるブログも見つけました。証拠もなしに決め付けるのはいかがなものかという気もしますが、様々な人の視点から物事を眺められるのがブログの長所ですから、ここまでは構いません。むしろ問題は、自作自演説が完全にあり得ないと分かった後に、記事を謝罪の上で撤回したブログを見たことがない点です。被害者に不当な嫌疑を着せた上、閲覧者には結果的に明らかな誤報を伝えてしまったのですから、表現の責任として読者に一言伝える程度はできないのでしょうか。そのような責任を取る気がなければ、最初から表現などしない方が賢明です。
 今回の「コンクリート詰め」炎上事件をめぐっては、複数のマスコミがインターネットを批判するかのような論調の主張をしています。例えば読売新聞は、「「表現の自由」には責任が伴う」という題名の社説を掲げています。これ自体は全くその通りで、私も上記で同じように主張しています。しかしながら、「表現の責任」を最も守っていないのは誰でしょうか。私が考えるに、それは「炎上」への加担者でも、上記に述べたようなブログの執筆者でもなく、新聞社などのマスコミに他なりません。
 マスコミの横暴はここでも散々取り上げてきましたので、もはや説明の必要もないでしょう。朝日新聞をはじめとした捏造報道は有名ですし、河野氏などの冤罪を作り上げたのもマスコミです。何かショッキングな殺人事件でも発生すれば、被害者の家や葬式にすらよってたかって押しかけ、不法侵入やタバコ・空き缶のポイ捨てなどの最悪な行為を行います。それでいて、遺族の手記にマスコミ批判の部分があれば、被害者を失った悲しみなどの部分はそのまま引用しておきながら、批判部分は隠蔽して報道します。読売といえば、尼崎・福知山線脱線事故の時に「社長を出せ」と怒鳴りつけた記者は確か読売でした。プロ野球ストの際には「超高額所得者のスト」なる社説を出していますし、同系列の日本テレビが視聴率調査で不正を働いた後にも、社説に「(テレビ朝日のダイオキシン報道や、他社の誤認報道などを取り上げて)他山の石としたい」と書いた際には「日テレ」の文字は1回も出てきませんでした。
 事例はまだまだ大量にありますが、ここではこれ以上は触れません。しかし、何とも格好がつかないことに、先のような「責任を取らない」ブログと似たような行為をしたマスコミがある点には触れておかなくてはなりません。
 イラクの話ばかりで恐縮ですが、以前に米国が「イラクは大量破壊兵器を45分以内に配備できる」と主張して戦争に踏み切りました。大量破壊兵器があるかさえ怪しいのに、米国はさらに「それを45分以内に配置できる」という立証責任までもを負っており、この時点で現実的にまずあり得ない主張であるのは明白でした。少なくとも私は、この時点であり得ないと確信していました。
 当時の首相である小泉氏は米国への支持を明確にしましたが、新聞社の論調は割れていました。そのうち、「イラク戦争には反対」「日米同盟維持のために賛成」といった意見には問題はありません。しかしながら、中には「イラクは大量破壊兵器を持っている」「兵器がテロリストに渡れば、日本の安全も脅かされる」などといった論調で戦争を正当化するマスコミもありました。急先鋒が産経新聞、その次が読売新聞です。
 今からすれば噴飯ものですが、当時は誰にも真実は分かりませんでした。何の根拠もなく大量破壊兵器があると断定し、その仮定を元に支持の理屈を作ったのは失敗でしたが、いくら報道たるものが慎重でなければならないとはいっても、勇み足は誰にでもあるものです。他ならぬ米国が「ある」と言い張っていたのですから、信じてしまっても無理はありません。
 しかし、問題はその後です。イラクをいくら探してみても、大量破壊兵器など影も形もなかったのです。「なさそう」が主流意見になっても、産経新聞はご丁寧にも米国の研究機関の論文を引用し、社説で「この通り、大量破壊兵器はある」と言い張っていました。しかし、産経新聞の論説委員の方々は英語は読めるようですが、米国の研究機関が「大量破壊兵器は存在する」と主張するのは当たり前であることには誰も気づかなかったようです。
 このように、産経新聞その他の新聞社は、社説でとはいえ明らかに正しくない情報を流し、読者に誤った認識を与えた恐れがあるにもかかわらず、それを謝罪・撤回したという話は聞いたことがありません。一方、ブッシュ氏のイラク戦争を支持した外国紙の中には、「なぜ我々はだまされたのか」などとする検証記事を書いたところもあるといいます。これこそ「表現の責任」を重んじるジャーナリズムというものですが、翻って日本はどうでしょうか。
 「表現の責任」は極めて大事です。事実無根の名誉毀損などを行えば、責任を取らなくてはならないのは当然です。インターネットであれ何であれ、表現の自由には表現の責任が付属しており、自分の表現には責任を持たなくてはなりません。表現に責任を持つことは、表現の信頼性を高める上でも重要です。しかしながら、マスコミには他者の「表現の責任」を指摘し、インターネットを批判する権利はありません。誰が最も表現の責任を踏みにじっているかを自覚し、恥を知るべきでしょう。

 まずは前回の「カット」の続きから。
 カットが一方通行であるのは前に述べました。ところで、カットの有効範囲はどこからどこまでなのでしょうか。ここに述語aがあるとして、仮にこの中から述語bを呼び出し、さらに述語cを呼び出し、述語cにカットがあったとします。述語cに一方通行があるからといって、aやbにも戻らなくなるのは動作として不自然です。そもそも、もしPrologが述語aからb、そしてcまで探索し、cの一方通行によってaにもbにも戻らなくなってしまうのなら、ほとんど行き倒れの旅人です。いくら探索中の不慮の事故とはいえ、これではあまりにも気の毒な処遇です。
 これを確かめるには、実際に試してみるのが最善でしょう。
way(right).
way(left).

a(A , B , C) :- way(A) , b(B , C).
b(B , C) :- way(B) , c(C).
c(C) :- way(C) , !.
 まず述語aに入り、左右に分かれた道のいずれかを通って述語bに行き着きます。bはさらに同様にしてcに行き着きます。cでも道は二手に分かれており、どちらに進んでも一方通行になっています。これを実行すると、いかなる結果が得られるのでしょうか。
?- a(A , B , C).
A = right,
B = right,
C = right ;
A = right,
B = left,
C = right ;
A = left,
B = right,
C = right ;
A = left,
B = left,
C = right.
 述語a及びbの道は左右両方を探索している一方、cでは右の道しか探索していないようです。これは、cの分岐点でまず右に進むと一方通行になっており、バックトラックして左の分岐には進めないためです。一方でaとbは何事もなく探索されていますので、カットの有効範囲は「述語毎」、さらに正しく表現すれば「呼び出し毎」であると考えられます。
 迷路に例えるなら、まず探検家Prologはaと呼ばれる洞窟を探検します。洞窟に入ると、道がrightとleftに分かれています。ひとまず右に進むと、今度は洞窟bの入り口に行き着きました。ここも道が二手に分かれており、こちらもひとまず右に進みました。すると、さらに洞窟cの入り口に行き着きました。これも道が分かれており、右に進むと一方通行でした。これによって先ほどの分岐点に戻って探索する必要のなくなったPrologは、これで洞窟cはすべて探索したと判断し、その場で洞窟cを脱出してbの通路に戻りました。bにはまだ分岐点が残っていますので、そちらの探索を行います。
 これは以下のような場合も同じです。
way(right).
way(left).

a(A , B , C) :- way(A) , b(B , C).
b(B , C) :- way(B) , c(C).
c(right) :- way(right) , !.
c(left) :- way(left) , !.
 述語c/1が2つあるからといって、見た目にだまされてはいけません。述語b内の「c(C)」が洞窟cに入る記述で、そこでc(right)とc(left)の2つの道が示されるのですから、先の場合と結果は変わらないのです。当然、カットによる一方通行で洞窟探検が続行できなくなれば、入り口まで戻ってくるでしょう。
 他には、以下のような変則的パターンも考えられます。
way(right).
way(left).

a(A , B , C) :- way(A) , b(B , C).
b(B , C) :- way(B) , c(C) , !.
c(C) :- way(C).
 実行結果はこの通りです。
?- a(A , B , C).
A = right,
B = right,
C = right ;
A = left,
B = right,
C = right.
 これもまた原則論そのままです。上記の問い合わせによってまず洞窟aに入り、左右への分かれ道をひとまず右に進みます。そこで洞窟bに入り、ここでも左右への分かれ道をひとまず右に進みます。そこで洞窟cを見つけて入り、またしても分かれ道でひとまず右を選び、ここで洞窟cを突き抜けてbに戻ります。どうやら洞窟cはトンネルになっていたようです。そして、ここでカットを見つけました。カットの効果で戻って探索する必要がなくなり、洞窟bの経路探索が終わりましたので、洞窟bから帰還します。これで洞窟aまで戻りますが、洞窟aの左の道はまだ探索していませんので、分かれ道までバックトラックして探索に向かいます。
 この辺りの仕様は論理型独特のものですので、手続き型や関数型の開発経験では感覚を得づらい部分もありますが、洞窟探検のつもりで考えてみましょう。

 関数型言語が最も得意とするものは何でしょうか。
 手続き型との差異は色々ありますが、関数型といえば1にも2にもリスト処理でしょう。関数型言語の元祖であるLisp自体、List Processorの略称とされています。そもそもLispではすべてがリストなのです。関数もリスト、マクロもリスト、マクロによって置き換えられるデータもリストです。
 その他の関数型言語はさすがにそこまで極端ではありませんが、多くの言語はリストの扱いに長けています。例えば、ほとんどの関数型言語にはmap関数が用意されています。リストの全要素に1を加えたいとすれば、Common Lispなら
(mapcar (lambda (x) (+ x 1)) '(1 , 2 , 3 , 4 , 5))
 とするでしょうし、カリー化ができる言語なら
map (+ 1) [1..5]
 と書いても構いません。手続き型ならforeachが使えそうですが、そうはいかない場合もあります。JavaにはSE 5からfor-each文が導入されましたが、プリミティブ型では
int list = {1 , 2 , 3 , 4 , 5};
for(int i : list)
	i ++;
 このようなことはできません。ここでのint iはコピーに過ぎないため、変更しても意味がないのです。
 その他、関数型のリスト処理の例としてよく引き合いに出されるのが、以下のようなHaskellのコードです。
-- クイックソート
-- 見やすくするために改行をはさんでいるが
-- 正しくは qsort (key : source) と qsort [] の2行
qsort (key : source) = (qsort (filter (< key) source)) ++
	(key : (qsort (filter (>= key) source)))
qsort [] = []

-- 動作テスト
main = qsort [5 , 1 , 7 , 3 , 2 , 9 , 8 , 6 , 4 , 0 , 5 , 8 , 2]
-- 結果 : [0,1,2,2,3,4,5,5,6,7,8,8,9]
 手続き型から関数型の世界に飛び込もうとすると、まず最初にこのような「2行クイックソート」を見せられて仰天することになります。手続き型であっても、見栄えを完全に無視すれば1行や2行にまとめられるはずですが、関数型では普通のスタイルで2行なのです。
 それでは、論理型言語たるPrologはどうでしょうか。無論、Prologにもリストは存在します。さらに、関数型言語に近い性質を持っているだけあって、リストのパターンマッチ処理など一通りの機能までもが使用できます
 まずリストを作成する方法から。
get_list([1 , 2 , 3]).
 後はこれをget_list(R)のように呼び出せば、上記リストがRに格納されますので、
?- get_list(R).
R = [1, 2, 3].
 無事に結果が得られます。なお、リストを変数に代入するには「=」を使用しましょう。「is」を使うとエラーが発生します。
 関数型言語でおなじみのパターンマッチも容易です。Lispのcar/cdr及びconsを作成するには、以下のように記述します。
car([X | _] , X).
cdr([_ | XS] , XS).
cons(X , XS , [X | XS]).
 実行すると次のようになります。
?- car([1 , 2 , 3] , R).
R = 1.

?- cdr([1 , 2 , 3] , R).
R = [2, 3].

?- cons(1 , [2 , 3] , R).
R = [1, 2, 3].
 パターンマッチの際、先頭から複数の値を取る記法も用意されています。以下はリストの先頭2つを取り、それを足し合わせる述語です。
?- plus([1 , 3 , 7 , 15] , R).
R = 4.
 これでパターンマッチが行えましたが、これだけでは面白くありません。せっかくパターンマッチをするのであれば、リストをforeachのように捜索する記述も行ってみたいものです。ここでは「リストの全要素の合計」を求める述語を書いてみましょう。
 末尾再帰を使わなければ、以下のようなコードになるでしょう。
sum([X | XS] , R) :- sum(XS , S) , R is S + X.
sum([] , 0).
 この種の処理では末尾再帰が通例ですので、末尾再帰を使うのであれば、
sum(XS , R) :- in_sum(XS , 0 , R).
in_sum([X | XS] , V , R) :- N is V + X , in_sum(XS , N , R).
in_sum([] , R , R).
 こうなります。
 いずれも呼び出し方は同じで、以下のようになります。
?- sum([1 , 2 , 3 , 4 , 5] , R).
R = 15.
 他のリスト操作も考えてみましょう。この記事の最初で「すべての要素に1を加える」処理が出てきましたが、Prologでこのような述語は書けるのでしょうか。
add(XS , RS) :- in_add(XS , [] , RS).
in_add([X | XS] , YS , RS) :- Y is X + 1 , in_add(XS , [Y | YS] , RS).
in_add([] , YS , RS) :- reverse(YS , RS).
 関数型におけるリスト処理のお手本のようなコードです。まずリストの先頭から値を1つ取り、それに1を加えて結果用リストの先頭に加えます。このままではリストが逆さになっていますので、Prologに用意されている述語であるreverse/2を使って反転しています。
 なお、もしreverse/2がなかったとしても、これは以下のようにして簡単に作れます(すでにreverseが存在する環境で名前がぶつからないようにするため、末尾にアンダーバーを入れてあります)。
reverse_(XS , RS) :- in_reverse_(XS , [] , RS).
in_reverse_([X | XS] , YS , RS) :- in_reverse_(XS , [X | YS] , RS).
in_reverse_([] , RS , RS).
 無事にプログラムが完成したところで、述語を実行してみましょう。
?- add([1 , 3 , 7 , 15] , RS).
RS = [2, 4, 8, 16].
 簡単なものです。
 次に、取り上げ忘れていたfail機能について。以前に
some(any).
 このような述語は、
some(any) :- true.
 これと同じであると述べました。
 Prologにはこれの対となる述語も用意されています。true/0の反対といえば、通常の言語からすればfalse/0が妥当なところで、実際にSWI-Prologではfalse/0も用意されています。しかし、false/0はSWI-Prologの独自拡張であるらしく、GNU Prologでは「そのような述語はない」旨のエラーが発生してしまいます。
 Prologにおいてtrue/0の対となる述語はfail/0です。この述語はその名の通り、マッチをその場で確実に失敗させます。迷路の例で言えば、「この先には進めません。引き返してください」と立て札が立ててあるようなものです。世の中にはひねくれた人間がいるもので、大抵は立て札を無視して進もうとする人が現れるものですが、大抵はろくな結果になりません。わざわざ「このはしは渡れません」と書いてあるのに、「はしを渡ってはいけないのだから、中央を渡ればよい」などと訳の分からない理屈によって橋を渡ろうとすると、大抵は途中から完全に通れなくなっており、無駄足になってしまう場合がほとんどです。当然ではありますが、Prologにひねくれ者や詭弁屋はいませんので、failを見つけた時点で必ず引き返してくれます。
 failはあくまで「道を引き返すよう書かれた立て札」に過ぎないため、failを見つけた時点で探索が終了するわけではありません。一旦引き返して分岐点まで戻った後、まだ入っていない道があれば、そちらも探索します。あまり利用価値はないかもしれませんが、もしその場で述語内部の探索自体を終了させたければ、
! , fail.
 このような記述となりそうです。カットによって通路が一方通行になっており、さらにその先は立ち入り禁止なのですから、もし公道でこれが行われれば大バッシングは必至ですが(しかもそのような場所に限って取り締まり警察官を配置)、Prologでは捜索を打ち切るだけですので、問題はありません。
 このfail、単に「条件を失敗させる」記述としても使用できるのですが、Prologをある程度手続き風に記述する際にも用いられているようです。PrologにもIO処理は存在し、標準出力への書き込みはwrite/1、改行の書き込みはnl/0、一方で標準入力からの読み込みはread/1で行われるのですが、このようなIO述語を使用すれば、条件が失敗したとしても出力や入力はなされますので、何らかの効果が得られます。
 例えば以下のような述語があるとして、
languages(prolog).
languages(ocaml).
languages(lisp).
 論理型言語の特性を用い、言語すべてを取得するのは簡単です。ただ単に以下のように問い合わせるだけです。
?- languages(R).
 複数の結果が得られますので、後はセミコロンですべて表示するだけです。  それでは、複数の結果をセミコロンで表示するのではなく、すべての結果を一気に標準出力に表示したい場合はどうするのでしょうか。実際、このような処理は手続き型言語では頻繁に行われますし、関数型でもHaskellでやるのは少々面倒ですが、OCamlなどでは普通に行われます。Prologでもこのような処理には需要があるはずです。
 今までになかった問題だけに、一見すると悩んでしまいそうですが、何のことはありません。failを使用すれば良いのです。
?- languages(R) , write(R) , nl , fail.
prolog
ocaml
lisp
false.
 簡単に解説を。まずlanguage(R)の分岐点に差し掛かりますが、道がprolog、ocaml、lispの3つに分かれています。最初にprologの道に入り、道なりにあった述語write/1によってそれを標準出力に書き込み、nl/0によって改行を書き込みます。しかし、ここで「立ち入り禁止」の看板が見つかりましたので、分岐点まで戻ります。分岐点にはまだ入っていない道がありますので、次はocamlの道に入って同様の操作を行い、さらにはlispの道にも入ります。これで探索は終わりましたので、falseを返して終了します。
 つまり、write/1などの述語によってIO処理を行いつつ、探索処理自体はfailで失敗させるようにすれば、パターンの列挙が可能になるのです。これは習得しておくと役に立つ概念ですので、頭に入れておいて損はないでしょう。
 次に、Prologで「A = 1 + 2」を実行すると、なぜかAは「3」ではなく「1 + 2」になりますが、これの使い道について。Prologでの代入(らしきもの)には「=」と「is」の2種類がありますが、これらの効能がそれぞれ異なるのは以前に述べました。
 ところで、Prologには他にも代入がなされるケースがあります。述語の変数に対して値を渡す際と、述語のパターンマッチで値を受け取る際です。それぞれ以下のようなケースが該当します。
% 値を渡す
give(R).
?- give(100).

% 値を受ける
take(100).
?- take(R).
 これらの処理においてなされる代入は、「=」のものと同じです。したがって、takeを
take(R) :- R = 100.
 このように書いても構いません。
 これが何を意味するのかといいますと、この動作には前回の記事でも触れましたが、述語の引数として「1 + 2」を渡すと、述語の側は「3」ではなく「1 + 2」を受け取るのです。つまり、Aに1を加えた値を述語some/1に渡したければ、普通の関数型言語のように「some(A + 1)」としてはいけません。「B is A + 1 , some(B)」としなくてはならないのです。
 逆に言えば、述語の側は渡したものを「そのまま」受け取ってくれるのですから、実装の一例として以下のような述語が作成できます。
when(EXPR , CODE) :- EXPR , ! , CODE.
 これは「EXPRの条件を満たした場合のみ、CODEを実行する」述語です。EXPRとCODEの間にカットがあるのは、もしEXPRが複数の道を有する分岐点になっていたら、何度もEXPR以降の処理を実行してしまう恐れがあるためです。
 これを使って、以下のような問い合わせを行ったとしましょう。
?- A = 1 , B = 0 , when(A > B , write('OK')) , fail.
OK
false.

?- A = 1 , B = 2 , when(A > B , write('OK')) , fail.
false.
 上記の通り、面白い結果が得られます。1つ目の引数の条件がtrueにならない限り、2つ目の引数の述語は全く実行されないのです。仮に実行されるのであれば、whenの1つ目の引数が失敗してもメッセージが表示されるはずですから、2つ目の引数が実行されていないのは明らかです。
 これはいわば、Lispのマクロのようなものです。「=」型の代入は、述語が実行されて結果が代入されるのではなく、述語そのものが代入されますので、
when(A > B , write('OK'))
 この呼び出しであれば「EXPR = A > B , CODE = write('OK')」となるでしょう。くどいようですが、この呼び出しの時点ではまだ述語は実行されません。whenに入ってはじめて、まずは「A > B」が実行され、それがtrueであればwrite('OK')が実行されます。
 この仕様のおかげで、PrologでもLispのマクロのようなテクニックが使用できるのです。一見すると意図が分からない仕様にも、実は深い意味があったのです。せっかくお膳立てがなされているのですから、この仕様を用いて手続き型にありがちな制御構文を書いてみましょう。まずはifから。
if(EXPR , CODE , ELSE) :- (EXPR , ! , CODE) ; ! , ELSE.
 まずEXPRで渡された述語を評価します。もし真であればカットを通ってCODEに移り、偽であるならカットを通ってELSEに移ります。こちらでもカットがなければ後戻りしてしまい、EXPRが真であってもCODE実行後にORの先にあるELSEまで実行してしまいます。気をつけましょう。使い方はLispのifと似たり寄ったりで、1つ目の引数の評価が真であれば2つ目、偽であれば3つ目の引数の述語を実行します。
?- if(1 > 0 , write('expr is true') , write('expr is false')).
expr is true
true.

?- if(1 < 0 , write('expr is true') , write('expr is false')).
expr is false
true.
 次に、単純な回数指定型ループを。
for_loop(MAX , CODE) :- in_for_loop(0 , MAX , CODE).
in_for_loop(I , MAX , CODE) :- I < MAX , CODE ,
	J is I + 1 , ! , in_for_loop(J , MAX , CODE).
 単純なループですが、CODEには述語を渡せます。
?- for_loop(5 , (write('Spam!') , nl)).
Spam!
Spam!
Spam!
Spam!
Spam!
false.
 fail/0の他に用いられる述語として、repeat/0なるものもあります。これは無限回廊のようなもので、いわば永久に探索が終了しない分岐点です。推測ですが、実装はおそらく以下のようなものではないでしょうか。
repeat_ :- true ; repeat_.
 仮に以下のようなプログラムを書き、
eternal(R) :- repeat , R = 10.
 問い合わせたとします。
?- eternal(R).
R = 10 ;
R = 10 ;
R = 10 ;
R = 10 ;
R = 10 .
 セミコロンを打ち続ける限り、永久に同じ結果が返ってきますので、ピリオドを打って終了しましょう。
 もしrepeatの動作が先の通りなら、これは次のように解釈できます。まず「true ; repeat」はORでつながれていますので、これはtrueの道とrepeatの道の2通りがある分岐点となります。まずはtrueの道に入り、trueですからそのまま道を進んでその先の探索を行います。探索が終わったら、ここの分岐点のうちrepeatの道にはまだ入っていませんので、そちらに向かいます。ところが、たどり着いたのは同じ分岐点でした。どうやら同じところに戻ってきてしまったようです。しかし、普通の人なら数回もすればいい加減に気づいて探索をやめるところですが、探索を打ち切ってはならないのがPrologの定めです。したがって、また同じ処理の繰り返しとなります。
 この無限回廊から抜け出すにはカットを使用します。trueの道を探索しつくした後、戻ってきてrepeatの道に入るから無限回廊となるのであって、trueの道の途中が一方通行になっていれば無限回廊に戻る必要がなくなりますので、カットの先を探索しつくした時点で処理が終了します。一方、途中で再びループの先頭に戻りたい(いわゆるcontinue)場合には、failで失敗させてrepeatまでバックトラックさせる方法があります。
 repeatには注意点もあります。repeatは無限回廊で、それゆえに全経路を探索することはできません。この性質により、repeatより以前にあった分かれ道に未到達経路があったとしても、repeat以前へのバックトラックはできません
 このような分かれ道の述語があるとして、
way(right).
way(left).
 以下のように問い合わせたとしましょう。ここまでの内容を正しく理解していれば、これの結果は考えるまでもなく分かるはずです。
?- way(A) , way(B).
A = right,
B = right ;
A = right,
B = left ;
A = left,
B = right ;
A = left,
B = left.
 当たり前のコードを書くなと怒られそうですが、それではway(A)とway(B)の間にrepeatをはさんだ場合はどうでしょう。
?- way(A) , repeat , way(B).
A = right,
B = right ;
A = right,
B = left ;
A = right,
B = right ;
A = right,
B = left ;
(以下、セミコロンを打つ限り無限に続く)
 way(A)では左の道に入っていないようですが、なぜでしょうか。一言で言えば、repeatをさかのぼってのバックトラックはできないためです。
 理由を解説しておきましょう。まずway(A)の分岐で右に進みます。そして、その先にあるのがrepeatの無限回廊です。trueの道とrepeatの道のうち、まずtrueの道に進みます。その先でway(B)の分岐を発見し、まず右に進み、次に左に進みます。これでway(B)の全経路を探索したので、バックトラックで無限回廊まで戻ります。ここの分岐のうち、trueの道は探索が終わったところですので、次はrepeatの道へと進みます。無限回廊ですから同じ場所に戻されるのですが、それでも探索をやめてはいけないのがPrologの定めです。またもtrueの道に進み、バックトラックし、repeatの道に進んで無限回廊を探索し続けます。永久に経路の探索が終わりませんので、それ以前へのバックトラックも機能しません
 repeatを使いつつ、way(A)とway(B)の経路探索も行いたいのであれば、以下のようになるでしょう。
way(right).
way(left).

a(A , B) :- way(A) , b(B).
b(B) :- repeat , ! , way(B).
 way(B)の前にカットがあるため、先のように無限ループには陥らず、way(B)の左右の道を探索した後にbを脱出します。したがって、結果は当然
?- a(A , B).
A = right,
B = right ;
A = right,
B = left ;
A = left,
B = right ;
A = left,
B = left.
 こうなります。
 repeatの構造を応用すると、forやforeachといったものも作成できます。例えばforeachが欲しければ、
foreach([A | XS] , R) :- R = A ; foreach(XS , R).
 こうなります。1つ目の引数がリスト、2つ目が取り出した値を受け取る変数です。Prologでは基本的に、1つの述語呼び出し内では変数の中身は一意でなければならず、「A = 1 , A = 2」なる記述は失敗しますが、逆に言えば「違う呼び出し内」でなら別の値を割り当てても構わないのですから、このような記述が可能です。
 実際に使用してみましょう。
?- foreach([1 , 2 , 3] , X) , P is X * X , write(P) , nl , fail.
1
4
9
false.
 確かにforeachが実行されています。最後のfailがなぜ必要かが分からない方は、この記事を最初から読んでみましょう。結論から言えば、failは別に存在しなくても動作はしますが、いちいちセミコロンを押して次候補を表示しなくてはなりません。また、repeat同様にカットでループを打ち切ったりもできます。
 このままではあまりに理解が難しいコードですので、例によって探索に例えた解説をしておきましょう。まず「foreach([1 , 2 , 3] , X)」が呼び出されると、Prologはforeach([A | XS] , R)に入っていきます。ここでRには未確定の変数Xが渡されていますので、これは当然未確定となります。foreachに入ると現れるのが「R = A ; foreach(XS , R)」なる分岐点です。まずはR = Aの道に進むことにして、Rは未確定の変数ですので、RにはAを代入します。R = Aはマッチしましたので、foreachのトンネルを抜けてforeach以降の探索を始めます。その探索が終わったら、先の分岐点までバックトラックを行い、もう一方の分かれ道であるforeach(XS , R)に入ります。以下、リストを全部取り出すまで無限回廊が続きます。
 COBOL使いの方には、これなどいかがでしょうか。
perform(VARYING , FROM , BY , UNTIL) :- FROM =< UNTIL ,
	(VARYING = FROM ;
	NF is FROM + BY , perform(VARYING , NF , BY , UNTIL)).
 多少はCOBOLらしさが出せたでしょうか。使い方はCOBOLとほぼ同じです。
% PERFORM VARYING IDX FROM 1 BY 3 UNTIL IDX > 10
?- perform(IDX , 1 , 3 , 10) , write(IDX) , nl , fail.
1
4
7
10
false.
 ちなみに、このような書き方もできたりします。参考までに。
perform(FROM , FROM , _ , UNTIL) :- FROM =< UNTIL.
perform(VARYING , FROM , BY , UNTIL) :- FROM =< UNTIL ,
	NF is FROM + BY , perform(VARYING , NF , BY , UNTIL).
 上の「perform(FROM , FROM , _ , UNTIL)」により、引数の1つ目と2つ目を同じ値と特定していますので、これがVARYING = FROMの作業を代行しています。このように書いたとしても、やっていることはほとんど同じです。もし違いがあるとすれば、「FROM =< UNTIL」が2回評価される程度のものでしょう(当然ながら同じ結果となるため、動作は特に変化しない)。Prologは結構色々な書き方ができますので、好みの方法で実装しましょう。
 今回はなかなかすさまじい内容となりましたので、この辺までにしておきましょう。つくづく簡単なプログラム言語など存在しないものです。
カテゴリ [開発魔法][社会問題][経済・知的財産] [トラックバック 0][コメント 0]

「道徳は文化だ」
2009/01/18(Sun)23:42:35
 息も絶え絶えの自民党にまたしてもスキャンダルが発生しました。官房副長官の鴻池氏が知人の女性に宿舎の鍵を渡し、宿泊させていたとの疑惑を週刊誌に報道されてしまったのです。不倫疑惑も持ち上がっており、野党の一部も辞任を求めるなどしていますが、鴻池氏はそのような関係ではないとしています。現在ではある程度幕引きが図られたようですが、またしても火種を持ち込んでしまった格好です。
 最初に断っておきますが、本件に何らかの裏がないのであれば、私はこれを辞任に値する失態とは考えていません。鴻池氏といえば「加害者の親は市中引き回しの上打ち首」の失言で有名な人物ですが、このような失言をまた行ったのであれば辞任に値します。少年犯罪の親にはある程度の監督責任があることは否めませんが、親が特に犯罪を指示したり、虐待を行っていたのでもなければ、親を「市中引き回しの上打ち首」にする法も道理も存在しません。法制度も刑事制度もまるで未発達の近代以前には、一家あるいは一族を根絶やしにしたり、何らかの罰を与えるといった刑も存在したようですが、このような理不尽極まりない刑罰の犠牲者の上にあるのが近代の法制度なのです。鴻池氏は他にも「犯罪者の親もテレビに映すべき」などと称していますが、これらの発言は政治家としての発言力を利用した私刑としか言いようがなく、全く救いようのないものでした。これをまたしても行ったのであれば、辞任は当然です。
 しかし、今回の問題はそのような発言ではありません。本人は否定していますが、仮にこれが不倫関係であったとしても、辞任に値するとはいえません。日本には不倫を裁くための刑法などなく、民事であれば関係者のみの問題に過ぎません。確かに、不倫であるならその行為が、そうでないとしても疑われるような行為が不適切で、政治家として自覚が足りないというのは事実ですが、それが直ちに辞任に結びつくわけではありません。そもそも倫理上の責任を問うのなら、鴻池氏より枡添氏の方がずっと問題のはずです。
 しかしながら、自民党がこの問題をもみ消す行為は看破できません。自党が崩壊寸前であることをかんがみてか、形式だけは厳重注意を行ったようですが、一部野党の辞任要求には応じる姿勢を見せておらず、基本的には「個人の問題」として収拾を図っています。確かにその通りで、本件はあくまで個人の問題に過ぎず、辞任要求に応じる必要も全くないのですが、自民党がそれを主張してはいけません
 なぜなら、自民党は独善的ともいえる道徳観や倫理観を振りかざし、そのために無辜の市民の人権を侵害することさえいとわない政党であるためです。本件において、鴻池氏の行為は辞任に値しません。また、これが個人の問題であるのも確かです。しかし、自民党がそれを称するのはダブルスタンダードというものです。
 最も代表的な例は、代理出産問題と民法772条規定です。私の個人的な意見は置くとして、確かに代理出産には倫理的な問題も多く、場合によっては法律で禁じたり制限したりする必要があるかもしれません。それでも代理出産を行った者に対しては、何らかの罰則を科すのも仕方ありません。しかし、現在の代理出産は子どもの人権を奪い去ることを抑止力にしている節があります。すなわち、出産者以外の人が母親になるには養子にするしかなく、養子にするのも困難な状況では全く無辜のはずの子どもが宙に浮く可能性があります。代理出産が許されないのであれば、実行者に罰則を科すのは仕方ありませんが、子どもに無用な被害を与える道理はどこにもなく、早急に解決しなければならない問題です。それを議員連中の道徳観や倫理観のために放置するなど、許されるわけがありません。
 とはいえ、代理出産は実際に倫理上の問題が大きく、実例も少ないためにさほど問題は表面化していないのですが、これよりさらにひどいのは772条問題です。実際に戸籍が作成できず、就学や医療で被害をこうむった人も実在します。元夫を相手取って裁判を起こそうにも、暴力を振るう元夫に住所を知られる可能性を考えて実行できない人は少なくないようです。事実、暴力夫が妻の実家に押しかけ、家人を殺害した例もありますので、この懸念はとても杞憂とはいえません。
 ところが、必要な教育や医療サービスが受けられないという、下手をすると生存権をも侵害しかねない問題が実際に起きているにもかかわらず、自民党は同規定の撤廃に慎重な姿勢を見せています。しかも、その理由として「元夫その他の法益の侵害」などといった点を主張するならまだ同意しますが、「性道徳が乱れかねない」からといった意味不明な意見を称する者までもが存在するのです。そのような独りよがりな政治家の道徳意識のために、全く無辜の子どもが戸籍を得られず、医療も教育も受けられないような状況に陥ることが許されるわけがありません。そもそも、生存権の侵害といった無辜の子どもの不利益を抑止力として、議員に個人の性道徳を指図されるいわれは全くありません。
 自民党には昔からこの種のおかしな意見が多く見られ、有名な例では「尊属殺人」の規定を削除する際にもひと悶着あったようです。1968年に発生した栃木実父殺し事件では尊属殺人の違憲性が争われ、1973年には違憲判決が下されたため、その後の尊属殺人に当たる事件はすべて殺人で起訴するようになり、またこれまで同罪で判決を受けた人には恩赦で減刑がなされたにもかかわらず、同罪の刑法からの削除については自民党内にお得意の「倫理」の主張が発生し、同罪は20年以上後の1995年にようやく削除されました。
 尊属殺人の問題はもう何十年も昔のものですので、当時は今より倫理の主張が強かったとしてもまだ納得できますし、実務上は使用されなくなるなど実害はありませんでしたが、772条などの問題では、何の問題行為も行っていないはずの無辜の子どもが生存権や教育権すら侵害される状況に陥っているのですから、独りよがりの倫理や道徳を掲げている場合ではないはずです。しかし、今すぐにでも与野党で同規定を削除・修正できるにもかかわらず、自民党は今でもそれをしようとはしません。言うなれば、親の問題(であると「道徳」を掲げる議員が考える)行為に対し、子が市中引き回しの上に打ち首にされているようなもので、何の道理も正当性もありません。何の関係もない子どもの生存権と教育権を、他者の法益保護のためではなく性道徳とやらのために侵害する行為には、極めて強い憤りを覚えます。
 そこまで道徳が大事であるなら、鴻池氏の行為はそれこそ万死に値するほど重いはずで、「個人の問題」などとはもってのほかです。しかし、鴻池氏の行為が辞任に値するほどとは私には考えられませんし、多くの人にとってもそうでしょう。自民党が氏を「個人の問題」として弁護するのも当然です。それでは何が間違っているかといいますと、自民党の一部議員の称する意味不明な「道徳」意識に他なりません。
 鴻池氏は辞任せずとも結構ですので、このダブルスタンダードは即座に解消すべきでしょう。「道徳」の文字が泣いています。

 麻生氏が税制改正関連法案の付則に消費税増税を盛り込もうとしていますが、一部の自民党議員は反対に回っている模様です。また、野党もこの方針を批判しています。ただ、民主党も増税の必要性は認識しているようで、鳩山氏は「政治、行政、経済が立ち直って、それでもお金が足りない時には消費税増税を国民にお願いするつもりだ」と述べています。
 付則については反対派及び野党の言う通り、やめた方が賢明でしょう。なぜなら、今からそれを付則に書き加えたところで、現段階では今後の状況が全く読めないためです。付則に明記はしていても、経済状況にあわせて柔軟に対応できるのなら構いませんが、付則にがんじがらめにされて最悪のタイミングで拙速な議論や行為を行い、それで景気を没落させるのでは意味がありません。かといって、付則を全く意に介さないのであれば、盛り込んでも意味はありません。
 しかし、消費税はいずれどうしても上げなければなりません。無駄を省くのは重要ですが、それにも限界がありますし、掛け声だけではどうにもなりません。上記の通り、それは自民党のみならず民主党も承知しています。ただ、消費税の増税には慎重であるべきなのは言うまでもありません。
 消費税増税でまず考えられるのは、年金全額税化のための増税ですが、これは基本的に許容されるべき増税でしょう。社会保障国民会議がまとめたインチキ試算によれば、消費税は少なくとも4.5%増税しなければならないといいますが、この種明かしは以前の記事を読んでいただくとして、この試算による企業の負担軽減分が実に3兆円(消費税1.5%分)、すなわち企業の負担を現在のままに据え置けば今のところ3%の増税で間に合うことを試算で堂々と示したのは、同会議の唯一かつ最大の功績といえるかもしれません。現状の保険料は定額という極めて逆進性の高い方法であるため、弱者救済としても優秀です。
 次に社会保障の充実ですが、こちらも反対すべき理由はありません。消費税の増税論は何かと批判を浴びますが、社会保障のロードマップをしっかり示した上で、増税後も間違いなくそれを履行すれば、多くの人はそれを支持するでしょう。消費税を増税し、それを社会保障に使うどころか、役人や悪徳大企業の私服を肥やしたりするために、強い批判を浴びるのです。現状の増税論が批判されるのも、そうなることが分かっているためでしょう。
 そこで、仮に増税がどうしても必要というのであれば、麻生氏は少なくとも次の3点、民主党は後の2点を確約し、またそのための具体的な方策やロードマップを示すべきでしょう。まずは金配りの撤回、次に消費税を社会保障にのみ使用することの確約、最後に法人税を下げないことの確約です。
 最初については説明する必要すらないとして、2点目は消費税を上げる動機からして必須です。社会保障目的の増税だから許されるのであって、無駄を増やすために増税するのではありません。そもそも、増税したものを社会保障費以外に流用されては、社会保障費は足りないままになりますので、また増税を言い出さなくてはなりません。このようなお粗末な事態を避けるためにも、これは必要です。
 そして3点目ですが、消費税を法人減税につぎ込む行為が認められないのは当然です。「消費税を上げて社会福祉に使い、同時に法人税も下げるべき」などといった詭弁もたまに見かけますが、消費税を原資に法人税を下げる行為を歪曲して表現しているに過ぎません。このような、社会保障をダシにして消費税増税を訴えつつ、その実法人税を下げるなどという行為は絶対に認められません。したがって、この確約が必要になります。
 また、景気回復目当ての法人税減税は行ってはいけません。非常に抜け道が多いものの、日本の法人税は40%程度とされますので、単純にこの通りとして考えてみましょう。仮に1億の利益が得られたとして、これをそのまま持っていると4000万円が引かれ、手元には6000万円しか残りません。だから景気の足腰を折ってしまう、と考えるのは早計というもので、もう少し深く収支を考える必要があります。
 手元に1億の利益を持っていても、税引き後は6000万円にしかなりません。しかし、仮に人件費や設備投資に1億円を使用すれば、6500万円の利益が期待できるとしましょう。そのまま持っていても6000万円にしかならないのですから、この投資には実行するだけの価値があります。一方、もし法人税が30%に下げられれば、税引き後は7000万円が残りますから、この人的・設備投資は行う価値がなくなります。さらに、もし法人税がタックスヘイブンのように極少ということになれば、1億の投資はまるまる1億以上の価値が期待できなければ行えなくなります。つまり、下手な減税は人件費と設備投資を冷え込ませます。大企業はそうであっても中小企業にそのような余裕はないと指摘されそうですが、中小企業は現在でも特例によって税が抑制されていますので、この論には最初から当てはまりません。
 消費税が社会保障ではなく法人税の減税に使用されれば、国民は状況がいっそうまずくなったものと考え、さらに景気が冷え込みます。国内景気が冷え込めば、輸出中心の大企業はダメージを抑制でき、国内中心の大企業も下請けや派遣切りでダメージを回避できますが、国内を中心にするしかない中小企業は大打撃をこうむります。
 すなわち、金配りをしないのは当然として、「消費税を社会保障に使用する」「法人税は下げない」の2点は両輪なのです。民主党も後々の増税をにらむなら、この方針を明確化すべきですし、麻生氏に至っては金配りを撤回するところから始めなければなりません。金配りを行いつつ増税を盛り込むなど、単なる荒唐無稽です。

 ジェネリックやOOP以外にも、Adaには色々な文法が存在します。かの悪名高いGOTOすら存在しています。
with Ada.Text_IO , Ada.Calendar;
use Ada.Text_IO , Ada.Calendar;

procedure Blog is
	sec : Day_Duration := Seconds(Clock);
	hours : Integer := Integer(sec) / 3600;

	begin
		if hours <= 6 then
			goto SHINYA;
		elsif hours <= 12 then
			goto GINKO;
		elsif hours <= 18 then
			goto HAKUCHU;
		else
			goto YAKAN;
		end if;

		<<SHINYA>>
		Put_Line("0-6");
		goto ESCAPE;

		<<GINKO>>
		Put_Line("7-12");
		goto ESCAPE;

		<<HAKUCHU>>
		Put_Line("13-18");
		goto ESCAPE;

		<<YAKAN>>
		Put_Line("19-23");
		goto ESCAPE;

		<<ESCAPE>>
		return;
end Blog;
 Adaは例外を備えていますので、特にGOTOが必要となる場面はないでしょう。
 これまでにwhile文や条件指定なしのloop文は散々使用してきましたが、ここでもう1つのループ構文であるfor文も取り扱っておきましょう。
with Ada.Text_IO , Ada.Integer_Text_IO;
use Ada.Text_IO , Ada.Integer_Text_IO;

procedure Blog is
	i : Integer := 0;

	begin
--		1 から 5 までのループ
		for i in 1..5 loop
			Put(i * i);
			New_Line;
		end loop;

--		10 から 6 までの逆ループ
		for i in reverse 6..10 loop
			Put(i * i);
			New_Line;
		end loop;
end Blog;
 応用次第でこのようなこともできます。
with Ada.Text_IO , Ada.Integer_Text_IO;
use Ada.Text_IO , Ada.Integer_Text_IO;

procedure Blog is
	arr : array(1..5) of Integer := (1 , 3 , 7 , 15 , 31);
	i , content : Integer := 0;

	begin
		for i in arr'First..arr'Last loop
			content := arr(i);
			Put(content * content);
			New_Line;
		end loop;
end Blog;
 次に例外について。Adaは83の時点から例外を備えており、おかげで複雑怪奇なGOTOなどを使用する必要はなくなっています。例外を投げるにはraise、受け取るにはexceptionを使用します。
with Ada.Text_IO;
use Ada.Text_IO;

procedure Blog is
	Sample_Exception : exception;	-- 例外の宣言

	begin
		Put_Line("例外のサンプル");

		begin
			raise Sample_Exception;
		exception
			when Sample_Exception =>
				Put_Line("例外をキャッチしました。");
		end;
end Blog;
 複数のwhenを並べて任意の例外をキャッチしたり、すべてのwhenに当てはまらなかった例外をキャッチすることもできます。また、受け取った例外をそのまま外側のスコープに投げる構文も存在しています。
with Ada.Text_IO;
use Ada.Text_IO;

procedure Blog is
	Sample_Exception_1 : exception;
	Sample_Exception_2 : exception;
	Sample_Exception_3 : exception;

	procedure Raising is
		begin
			raise Sample_Exception_3;
		exception
			when others => raise;	-- そのまま外部に投げる
	end Raising;

	begin
		Put_Line("例外のサンプル");

		begin
			Raising;
		exception
			when Sample_Exception_1 =>
				Put_Line("Sample_Exception_1 をキャッチ。");
			when Sample_Exception_2 =>
				Put_Line("Sample_Exception_2 をキャッチ。");
			when others =>
				Put_Line("その他の例外をキャッチ。");
		end;
end Blog;
 実に簡単です。書き方こそ少々独特ですが、やっていることはJavaのtry-catch-finallyと同等です。
 ただし、例外のサポートは当時でこそ先進的でしたが、現代の言語からすれば貧弱に見えなくもありません。C++やPerlの例外は好きなものを渡せますし、JavaやC#といった現代型のOOP言語では、ExceptionやThrowableなどといったクラスを継承したものを例外とみなすのが一般的です。これらの言語における例外は任意のデータを返せるため、その気になればreturn文の代わりに使用したりもできるのですが、例外のスローにはオーバーヘッドが存在する上、これを例外処理以外の用途に使用するとプログラムが極めて読みづらくなりますので、例外には文字列などエラーの情報を格納するのが普通です。
 それでは、Adaはエラーメッセージすら取ることができないのでしょうか。そうであればデバッグに難儀しそうですが、実際にはメッセージの使用が可能です。
with Ada.Exceptions , Ada.Text_IO;
use Ada.Text_IO;

procedure Blog is
	Sample_Exception : exception;

	begin
		raise Sample_Exception with "Sample Message";
	exception
		when e : Sample_Exception =>
			Put_Line(Ada.Exceptions.Exception_Message(e));
end Blog;
 raise with構文は、Javaでいうところのnew Exception(String)に相当します。後はException_Message関数でそのメッセージを取得するだけです。
 その他、Adaにはかの有名なcase文も搭載されています。switch文ではなくcase文ですが、機能はswitchと同等です。caseには分散型のみが許容されており、文字列やレコードといった合成型は使用できません。
with Ada.Text_IO;
use Ada.Text_IO;

procedure Blog is
	type RGB is (Red , Green , Blue);

	color_type : RGB;
	begin
		color_type := Red;

		case color_type is
			when Red => Put_Line("#FF0000");
			when Green => Put_Line("#00FF00");
			when Blue => Put_Line("#0000FF");
		end case;
end Blog;
 C言語などのswitchにおいては、caseに該当する項目がなければswitch文をスルーするようになっています。
int i = 3;
switch(i){
case 1:
case 2:
case 4:
	// ...
}
// i は case のすべてに当てはまらないので、switch では何も実行されない
 しかしながら、Adaのcaseではwhenがその型で取りうるあらゆる値を網羅していなければならず、さもなければコンパイルエラーとなってしまいます。先ほどのコードを例に取れば、RedとGreenの場合のみ処理を行いたいとしても、Blueの場合も書かなくてはなりません。また、例えば整数をcaseで使用するのであれば、AdaのIntegerで扱えるすべての範囲のwhenをcase文に書かなくてはなりません。
 このような場合に使用するのが、先ほどの例外処理でも登場したothersです。これはswitchでいうdefaultと同じ効果を持っています。
with Ada.Text_IO;
use Ada.Text_IO;

procedure Blog is
	i : Integer := 3;
	begin
		case i is
			when 1 => Put_Line("One");
			when 2 => Put_Line("Two");
			when 4 => Put_Line("Four");
			when others => Put_Line("Others");
		end case;
end Blog;
 特に何も処理する必要がなければ、nullと書いておきましょう。
case i is
	when 1 => Put_Line("One");
	-- ...
	when others => null;
end case;
 CやJavaに比べると少々面倒ではありますが、Adaはコンパイル時にエラーを投げてくるだけまともで堅牢といえるでしょう。C++の後釜を狙っているらしい某言語に至っては、switchにおいてdefaultを書き忘れても堂々とコンパイルが通り、しかもswitchのcaseに該当する値がなければその場でプログラムが落ちるという最悪の実装となっています。
 Cでいうswitchの機能に加え、Adaのcase文は範囲指定や複数値の指定の機能も保持しています。フォールスルーの機能はなさそうですが、そこは仕方ありません。範囲指定は配列と同様に「1..10」などと指定し、複数値は「A | B」のように指定します。
with Ada.Text_IO;
use Ada.Text_IO;

procedure Blog is
	type Language is (Ada , COBOL , Java , SQL ,
		Lisp , Haskell , OCaml , XQuery , Prolog);
	l : Language;
	begin
		l := Ada;
		case l is
			when Ada..Java => Put_Line("手続き");
			when Lisp..OCaml => Put_Line("関数");
			when SQL | XQuery => Put_Line("問い合わせ");
			when others => Put_Line("その他");
		end case;
end Blog;
 Adaはメモリの動的確保やオブジェクト指向プログラミングができる言語ですが、動的確保したメモリは基本的に自分で破棄しなくてはなりません。すなわち、どうしてもリソースを解放したい場面が多くなりますので、何としてもデストラクタが欲しいところです。果たしてAdaにデストラクタは存在するのでしょうか。
 Adaでデストラクタを使用するには、Ada.Finalizationパッケージを使用します。AdaICの7.6によれば、このパッケージの仕様は以下の通りです。
package Ada.Finalization is
	pragma Preelaborate(Finalization);
	pragma Remote_Types(Finalization);

	type Controlled is abstract tagged private;
	pragma Preelaborable_Initialization(Controlled);

	procedure Initialize (Object : in out Controlled) is null;
	procedure Adjust     (Object : in out Controlled) is null;
	procedure Finalize   (Object : in out Controlled) is null;

	type Limited_Controlled is abstract tagged limited private;
	pragma Preelaborable_Initialization(Limited_Controlled);

	procedure Initialize (Object : in out Limited_Controlled) is null;
	procedure Finalize   (Object : in out Limited_Controlled) is null;
private
	... -- not specified by the language
end Ada.Finalization;
 Initializeは初期化時、Finalizeは破棄時に呼ばれるプロシージャです。Adjustは代入コピー時に呼ばれるもので、limitedにつき代入のできないLimited_Contolledには提供されていません。これらの型を継承するなり、上手く使用するなりすることで、デストラクタが実装可能になっています。
 最も簡単な使用方法は、上記の型からデストラクタを実装したい型を派生させることです。
with Ada.Text_IO , Ada.Finalization;
use Ada.Text_IO , Ada.Finalization;

procedure Blog is
	package Destructor_Sample is
		type Some is new Controlled with null record;
		procedure Finalize(Object : in out Some);
	end Destructor_Sample;

	package body Destructor_Sample is
		procedure Finalize(Object : in out Some) is
			begin
				Put_Line("破棄されました。");
		end Finalize;
	end Destructor_Sample;

	use Destructor_Sample;

	s : Some;

	begin
		null;
end Blog;
 すでに特定の型を継承しているなど、上記のように直接継承するのが困難な状況であれば、デストラクタの機能を持つデータをレコード内に含めるか、あるいはレコードをデストラクタの継承レコードでラップするなど、多少の工夫が必要でしょう。
with Ada.Text_IO , Ada.Finalization;
use Ada.Text_IO , Ada.Finalization;

procedure Blog is
	package Destructor_Sample is
		type My_Controlled is new Controlled with null record;
		procedure Finalize(Object : in out My_Controlled);

		type Some is tagged
			record
				finalizer : My_Controlled;
		end record;
	end Destructor_Sample;

	package body Destructor_Sample is
		procedure Finalize(Object : in out My_Controlled) is
			begin
				Put_Line("破棄されました。");
		end Finalize;
	end Destructor_Sample;

	use Destructor_Sample;

	s : Some;

	begin
		null;
end Blog;
 この機能を使用した際、いつもながら「単なる思いつき」で参照カウント付きスマートポインタを書いたところ上手くいってしまいましたので、以下に実装を示します。
with Ada.Text_IO , Ada.Integer_Text_IO ,
	Ada.Finalization , Ada.Unchecked_Deallocation;
use Ada.Text_IO , Ada.Integer_Text_IO , Ada.Finalization;

procedure Blog is
	generic
		type Object_Type is private;
		type Name_Type is access all Object_Type;
	package Smart_Pointers is
		type Access_Natural is access all Natural;
		type Smart_Pointer is private;

		function Get(sp : in Smart_Pointer) return Name_Type;

	private
		procedure Free is new Ada.Unchecked_Deallocation(
			Object => Object_Type , Name => Name_Type);
		procedure Free is new Ada.Unchecked_Deallocation(
			Object => Natural , Name => Access_Natural);

		type Smart_Pointer is new Controlled with
			record
				data : Name_Type;
				counter : Access_Natural;
		end record;

		procedure Initialize(Object : in out Smart_Pointer);
		procedure Adjust(Object : in out Smart_Pointer);
		procedure Finalize(Object : in out Smart_Pointer);
	end Smart_Pointers;

	package body Smart_Pointers is
		function Get(sp : in Smart_Pointer) return Name_Type is
			begin
				return sp.data;
		end Get;

		procedure Initialize(Object : in out Smart_Pointer) is
			begin
				Object.data := new Object_Type;
				Object.counter := new Natural;
				Object.counter.All := 1;
		end Initialize;

		procedure Adjust(Object : in out Smart_Pointer) is
			begin
				Object.counter.All := Object.counter.All + 1;
		end Adjust;

		procedure Finalize(Object : in out Smart_Pointer) is
			begin
				Object.counter.All := Object.counter.All - 1;

				if Object.counter.All = 0 then
					Free(Object.data);
					Free(Object.counter);
				end if;
		end Finalize;
	end Smart_Pointers;

--	使用例
	type Access_Integer is access all Integer;

	package Int_Smart_Pointers is new Smart_Pointers(
		Object_Type => Integer , Name_Type => Access_Integer);

	use Int_Smart_Pointers;

	sp1 : Smart_Pointer;

	begin
		Get(sp1).All := 10;

		declare
			sp2 : Smart_Pointer := sp1;
		begin
			Get(sp2).All := Get(sp1).All * 2;
		end;

		declare
			sp3 : Smart_Pointer;
			sp4 : access Smart_Pointer;
		begin
			sp3 := sp1;
			sp4 := new Smart_Pointer;

			Get(sp4.All).All := 20;
			Get(sp3).All := Get(sp3).All + Get(sp4.All).All;
		end;

		Put(Get(sp1).All);
		New_Line;
end Blog;
 循環参照のような場合を除き、確保されたメモリは自動的に破棄されます。
 最後に「拡張return」についても扱っておきましょう。Adaでは代入と比較を禁じるlimitedが存在し、これはこれで便利な局面も少なくないのですが(Javaでクローン生成を許可しないものと考えると分かりやすい)、ここでAda特有の問題が発生します。limitedレコードは関数の返り値にできないのです。関数はその性質上、返り値を変数に代入しなければなりませんので、どうしようもありません。単に関数が使えないだけならプロシージャのout引数で代用できますが、演算子に至っては手の打ちようがありません。
-- コンパイルエラーが発生するコード
with Ada.Text_IO , Ada.Integer_Text_IO;
use Ada.Text_IO , Ada.Integer_Text_IO;

procedure Blog is
	type Integer_Data is limited
		record
			data : Integer;
	end record;

	function "+"(a , b : in Integer_Data) return Integer_Data is
		c : Integer_Data;
		begin
			c.data := a.data + b.data;
			return c;
	end "+";

	a , b , c : Integer_Data;
	begin
		a.data := 10;
		b.data := 20;
		c := a + b;
		Put(c.data);
		New_Line;
end Blog;
 そこで使用できるのが拡張returnです。
with Ada.Text_IO , Ada.Integer_Text_IO;
use Ada.Text_IO , Ada.Integer_Text_IO;

procedure Blog is
	type Integer_Data is limited
		record
			data : Integer;
	end record;

	function "+"(a , b : in Integer_Data) return Integer_Data;

	function "+"(a , b : in Integer_Data) return Integer_Data is
		begin
--			拡張 return
--			do 以下には好きなように処理を記述する
			return result : Integer_Data do
				result.data := a.data + b.data;
			end return;
	end "+";

	a , b : Integer_Data;
	begin
		a.data := 10;
		b.data := 20;
		declare
			c : Integer_Data := a + b;
		begin
			Put(c.data);
			New_Line;
		end;
end Blog;
 ついでにレコードの初期化に関しても触れておきます。Cでは構造体を作成と同時に初期化できましたが(C++はコンストラクタの使用が一般的)、Adaでも類似の処理が用意されています。
 最も単純な初期化方法は以下のようになります。
with Ada.Text_IO , Ada.Integer_Text_IO;
use Ada.Text_IO , Ada.Integer_Text_IO;

procedure Blog is
	type Tree_Data is
		record
			left : Integer;
			right : Integer;
	end record;

	tree : Tree_Data := Tree_Data'(10 , 20);

	begin
		Put(tree.left);
		New_Line;
end Blog;
 あるいは名前つきで指定することもできます。名前つきの指定の場合、変数の順番は任意です。
tree : Tree_Data := Tree_Data'(right => 10 , left => 20);
 この初期化方法では、レコードのすべての変数に値を与えなければなりません。もし値を与え忘れるとコンパイルエラーとなります。ただし、others => <>の記述があれば、指定が抜けていても構いません。
tree : Tree_Data := Tree_Data'(left => 10 , others => <>);
 以上、ここまでにかなりの分量を扱ってきましたが、Adaにはまだまだ様々な機能が存在します。せめて直列化や様々な属性などには触れたかったのですが、全部扱っていてはきりがなくなってしまいますので、Adaはひとまずここまでとします。Ada自体はいくら掘り下げてもまだ足りない言語ですので、いずれ他の機能を使用することもあるでしょう。
カテゴリ [開発魔法][社会問題][経済・知的財産] [トラックバック 0][コメント 0]

政争より自己利益
2008/12/24(Wed)19:58:11
 どうやら麻生氏は、国民のことなど何一つとして考えてはいないようです。私は麻生氏について、先の総裁選に立候補した時点どころか、安部内閣後の総裁選の時点から全く評価していませんでしたが、政権運営や政策でしくじるだけならまだしも、目前に迫った喫急の課題にすらろくな対応を行わないほど能のない人間とまでは考えていませんでした。想像以上に劣悪な政権であると言わざるを得ません。
 野党が提出していた緊急雇用対策法案が、与党によって抹殺されてしまいました。同法案は派遣切りなどへの緊急対策を盛り込んだもので、住居の手当てその他の規定が盛り込まれており、一刻も早く成立させなければならないものでした。それをあっさり廃案にしてしまうのですから、麻生氏はまともな政権運営や政策の実現ができないばかりか、今すぐやらなくてはならないことすら実行できない人間であるといえるでしょう。
 無論、私は何も野党案を「丸のみ」すべきと主張する気はありません。しかし、派遣切りなどへの対応は一刻を争う喫急の課題であり、すぐにでも対策が必要であることは与野党ともに分かっているはずです。それなら、与党の側もすぐに緊急対策をまとめて対案を出し、妥協すべきところは互いに妥協して、一刻も早く法案を成立させるのが政府・与党の取るべき行動であるのは疑いようもありません。
 与党の立場からすれば、野党がパフォーマンス目的で法案を出してきたことに対する警戒もあるのでしょう。しかし、いずれにせよ一刻も早く成立させなければならない法案を、「野党の政争の具だから」という理由で成立させないのであれば、それこそ雇用問題を政争の具にする行為です。今すぐにでも成立させなければならない法案に対してさえも「何でも反対」を決め込むのが、この状況下の内閣が取るべき行動なのでしょうか。
 麻生氏は「政争より財政」などとして、相変わらず解散を先延ばししようとしているようですが、そのようなもっともらしいことを言いながら、雇用問題を政争の具にしているのは一体誰なのでしょうか。自身の政争のために緊急雇用対策法案を葬っておきながら、都合の良い時だけ不況を言い訳に使用するのですから、あきれる以外にありません。どこから見ても明らかではありますが、麻生氏はただ自己都合のためだけに解散を先送りしており、不況はその理由として利用しているに過ぎません。本当に経済を立て直すために解散を先送りしているのなら、緊急雇用対策法案の1つもまとめられないわけがありません
 また、ほぼあり得ない仮定ではありますが、もし麻生氏が本当に経済を理由として解散を先送りしているのであっても、これは先送りの理由にはなりません。現在の政局を船舶に例えるなら、今の日本は難所に差し掛かった状態といえます。麻生氏の舵取りでは座礁の可能性が高く、それを避けるには舵取り役を選びなおすのが筋です。ところが、麻生氏は「このままでは座礁しかねないから」と主張して、舵から手を離そうとしないのです。まさに論理が破綻していると言うよりありません。
 現段階で確定的に書くのは少々はばかられますが、少なくとも麻生氏に関しては、これ以上のまともな政権運営はまず不可能と考えて間違いないでしょう。緊急雇用対策法案の1つもすぐに実行できない政権が、今後の難問に対処できるわけがありません。野党と調整して対策法案を出すのが最善策、野党案の丸のみが次善の策、野党に圧力をかけて与党主導の案を通すのがその次として、野党の案を「何でも反対」で消し去りつつ、自分は何もしないのは愚策の愚策、最悪の方法です。意地になって政権運営を続けたところで、今後ともこれの二の舞、三の舞となるだけでしょう。
 麻生内閣としては、金配り政策によって(その金は国民のものであるのに)国民に金を恵んで支持率を回復し、それに乗じた解散を描いているのでしょうが、このような作戦が成功するとは考えられませんし、成功してもらっては困ります。私は金配りにだまされるほど国民は愚かではないと信じますが、仮にそのせいで麻生氏が勝利するようなことがあれば、「支持が落ちたら国民に施し」が常態化しかねません。
 それにしても、麻生氏には日々新たな失望をさせられます。総裁選では結果の分かりきった出来レースを展開し、緊急雇用対策法案は野党案を廃案にして自分は何もせず、金配りでは国民の金を国民に配ろうとしています。解散を先延ばしし、この上にまだ何か行う気なのでしょうか。

 FRBがついにゼロ金利と量的緩和に踏み切ることを決定しました。あまりにも異例のケースで、まさに少し前の日本のような状態といえるでしょう。日銀も金利の引き下げを行うとともに、コマーシャルペーパーを買い取る量的緩和を行うようです。
 ところで、今回の不況がかなりの規模であることは周知の事実ですが、それにしても日本の混乱の仕方には異常なものがあります。日本はバブル崩壊後、不良債権などを処理し、様々な合理化や削減を行い、筋肉質になったといわれていました。バブル崩壊以前に比べれば、それなりのダメージにも耐えられると見られていました。ところが、いざその状況になってみれば、まだ序の口かもしれない波の1つで上へ下への大混乱です。日本経済は明らかに不況への耐性を失っているといえるでしょう。
 かつての日本では、規制緩和が異常なまでにもてはやされていました。「規制は悪、規制緩和は善」なる半ば狂信的なフレーズが唱えられ、規制の必要性が省みられないような状況が、ほんの数年前まで続いていたのです。その間に、派遣規制など様々な規制が次々と緩和され、現在の状況に至っています。
 確かに、規制の中には廃止すべきものも少なからず存在します。過剰な規制で「護送船団」を作っていては、必要なだけの競争原理も機能しなくなりますし、不況や変化に耐えうるだけの体力もつきません。こうした不要な規制の代表格としては、新聞の特殊指定などが挙げられます。特殊指定廃止が議論されるたび、新聞社が総出でキャンペーンによる世論誘導を行い、廃止論をつぶしてしまうことからしても、新聞社にとって同指定がどれほど強力な利権になっているかが分かります。このような規制は百害あって一利なしで、迅速に廃止すべきものです。
 しかしながら、規制は廃止すべきものばかりではありません。タクシー規制の撤廃などで現場が混乱した話はご存知の方も多いでしょう。そして、廃止してはならなかった規制の代表格は、やはり派遣の規制でしょう。また、実際にはすんでのところで未遂に終わったものの、実行されかけた不適切な規制緩和の代表格として、無賃残業合法化があります。
 ともかく、しばらく規制緩和の流れは続きました。最近は転換期にあるようですが、安部内閣で無賃残業合法化が成立寸前まで行ったことからすれば、安部内閣の時点でもまだ推進期にあったと見るべきでしょう。そして、不良債権の処理や合理化に加え、派遣などの様々な規制緩和によって、日本経済は贅肉を落として筋肉質となり、不況に強い日本経済が作られたはずでした。
 ところが、日本経済は不況に強くなるどころか、不況に対して極めて脆弱になっていたのです。「ネットカフェ難民」が生じ、不況の一波で何千何万の人が住居までもを追われ、日本は激震状態にあります。規制が緩和されていなければ、それはそれで問題が発生していたであろうことは否めませんが、ここまでの激震に至った理由として、規制緩和に原因の一端があることもまた事実でしょう。つまり、無用な規制緩和は国民にダメージを与えるばかりか、日本経済に対してもジリジリと侵食を始め、悪影響を及ぼし始めているのです。
 つまり、日本経済を保護するために規制の強化は不可避の状況であるといえます。もともとは経済状況の改善や強化のために規制が緩和され、その経緯で「規制は悪」などと言われたものでしたが、その規制緩和がめぐりめぐって日本経済に大打撃を与えているのですから、皮肉なものです。
 一部の企業は未だに派遣の更なる規制緩和を主張しており、中には「不適切な派遣に関しては、企業の自主的な取り組みに任せるべき」との主張もあるようですが、まさに噴飯ものです。なぜ派遣が問題視される状況になったのかといえば、その企業連中が派遣を散々に使い捨てにしてきたからに他なりません。最初からそのようなことをしなければ、見直し議論が発生する事態にもならずに済んだでしょう。このような主張は、マスコミの称する「自主規制」と同等程度に信用できず、規制は絶対に必要です。
 今回の不況による激震は、「過剰な規制緩和は経済を弱体化させる」という貴重な教訓といえるでしょう。

 MySQL 5.1のGA版が登場しました。未だにFalconとMariaはGAでは公開されていませんので、残念ながらこれらを使用することはできませんが、基本的には両者ともInnoDBと似たようなものとみられますので、それは正式リリース後で構わないでしょう。ちなみに、位置づけとしてはFalconはInnoDBの後継(InnoDB提供元のInnoBaseがOracleに買収され、ライセンスが更新できない恐れがあるため)、MariaはMyISAMの後継らしいのですが、Mariaはトランザクションや外部キーなどMyISAMでは使えない各種機能が使える(ようになる)模様です。
 インストール方法や設定iniの書き方はほとんど5.0と変わっていませんので、5.0からの移行は極めて簡単です。ただし、5.0をアンインストールする際には、5.0に関するサービスをすべて削除しておかなければ、5.1のInstance Config Wizardがエラーを投げてきます。通常のサービスは5.0のアンインストール時に自動的に削除されますが、MAX版を自分でサービス登録した場合には注意が必要です。なお、5.1では5.0のMAXの機能が通常のmysqldに統合されており、BLACKHOLEなどのエンジンを使用可能です。
 ところで、5.1における主要な変更点は何なのでしょうか。ストアドルーチンやトリガは5.0の時点で使用できますし、GAにはMariaやFalconも入っていません。トランザクションや外部キーがInnoDBで使用できるのは昔からです。これでは5.0とほとんど変わりません。
 このように、確かに5.1ではあまり目新しい機能は提供されていません。あれほど開発が遅れたにもかかわらず、残念なことです。しかしながら、5.1には目玉となるであろう機能が1つ存在します。それこそがパーティションです。
 パーティションとは、1つのテーブルのデータを分散して保存する機構です。本来なら1つのテーブルに保存されるデータを、いくつかに分けて保存することで、上手く使えば検索時間の削減を期待できます。
 例えば、次のようなテーブルがあるとしましょう。
id	lang
1	Scheme
2	Ada
3	COBOL
4	OCaml
...
 この中から特定のidのデータを得ようとすれば、それを検索する必要があります。この場合、おそらくidはPRIMARY KEYにされているはずですから、インデックスを使用した検索は可能なのですが、データ量が多くなると検索にも時間がかかるようになります。
 そこでパーティションの出番です。上のデータを、例えば2つに分けて管理します。
Partition-A: id % 2 == 0 のデータ
2	Ada
4	OCaml

Partition-B: id % 2 == 1 のデータ
1	Scheme
3	COBOL
 これにより、データはおおむね半分ずつ(idが連番であれば)に分割されますので、特定のidを持つレコードの検索時には、半分のデータの中から検索するだけで済むようになります。なお、langカラムを使用して検索するような場合には、上記のようなパーティションは何の役にも立ちません。パーティションは作成時にどのように作成するかの指定が可能ですので、デザインが重要といえるでしょう。
 それでは最も単純なHASHパーティションから。これは、テーブルを上記の原理で分割するものです。
CREATE TABLE langs(id INT AUTO_INCREMENT , lang VARCHAR(32) ,
PRIMARY KEY(id)) PARTITION BY HASH(id) PARTITIONS 2;
 ここでは「PARTITIONS 2」と記述していますので、パーティションは2つとなります。3や4、あるいはそれ以上の数値を指定しても構いません。その場合には、それぞれ「ハッシュ値をパーティションの数で割った余剰」が分類に用いられます。
 それではデータを登録してみましょう。
INSERT INTO langs VALUES(NULL , 'Ada') , (NULL , 'Haskell') ,
(NULL , 'Scheme') , (NULL , 'OCaml') , (NULL , 'F#') ,
(NULL , 'Groovy') , (NULL , 'Common Lisp') , (NULL , 'Prolog') ,
(NULL , 'Erlang') , (NULL , 'COBOL');
 正常に登録できたようです。それでは早速動作を確認、してみたいところなのですが、困ったことに確認のしようがありません。パーティションは単にテーブルを分けるだけであって、INSERTもSELECTも普通に使用した場合と同じなのです。当然ではありますが、テストできないのは困ったものです。
 ひとまずSELECTでも打ってみましょう。
SELECT * FROM langs;

2	Haskell
4	OCaml
6	Groovy
8	Prolog
10	COBOL
1	Ada
3	Scheme
5	F#
7	Common Lisp
9	Erlang
 通常、MySQLのORDER BYなしSELECTでは登録順にデータが表示されるものなのですが(ただしSQLは順番を保証していません。ORDER BYを書かずに順番に頼る実装をしてはいけません)、このSELECTでは偶数のデータの後に奇数のデータとなっています。どうやらパーティションは上手く動作しているようです。
 このパーティションは「パーティションの数で割った余り」によってデータを分類するため、AUTO_INCREMENTのように連番となるデータに適しています。データの値が偏る場合には、他の分割方法を用いた方が効率的な場合があります。
 次にRANGEパーティションを。これは「特定の範囲のデータをパーティションに格納する」ものです。0〜10、11〜20、21〜30、31〜といった具合に分割します。
CREATE TABLE persons(id INT , name VARCHAR(32) , birth DATE)
PARTITION BY RANGE(YEAR(birth)) (
PARTITION y1940 VALUES LESS THAN(1940) ,
PARTITION y1960 VALUES LESS THAN(1960) ,
PARTITION y1980 VALUES LESS THAN(1980) ,
PARTITION y2000 VALUES LESS THAN(2000) ,
PARTITION yother VALUES LESS THAN MAXVALUE);
 今回idがPRIMARY KEYでないのは、テーブル内にPRIMARY KEYがある場合、パティションはそれを含まなければならないためです。
 それでは、適当にデータを登録してみましょう。
INSERT INTO persons VALUES(1 , 'Ada' , '1815-12-10') ,
(2 , 'John' , '1920-1-10') , (3 , 'Cate' , '1932-6-12') ,
(4 , 'Mike' , '1944-8-12') , (5 , 'Merry' , '1956-7-3') ,
(6 , 'Bill' , '1972-5-4') , (7 , 'Pat' , '1988-9-28') ,
(8 , 'Linda' , '1990-4-22') , (9 , 'George' , '2001-2-9');
 せっかく登録してはみたものの、これまたパティションが使われているかを確かめる方法はありません。残念なことです。
 ところが、実はこのRANGEパーティション、任意のパーティションの削除ができるのです。特定のパーティションを削除すると、そのパーティションに属するデータがすべて削除されます。パーティション1つを丸ごと削除するため、大量のデータを削除する際にはDELETEより速いようです。
 それでは、上記のパーティションからy1940、すなわち「1940年未満」のパーティションを削除してみましょう。
ALTER TABLE persons DROP PARTITION y1940;

SELECT * FROM persons;

4	Mike	1944-08-12
5	Merry	1956-07-03
6	Bill	1972-05-04
7	Pat	1988-09-28
8	Linda	1990-04-22
9	George	2001-02-09
 この通り、しっかり削除されています。
 このパーティションはデータを範囲ごとに区切って保存するため、範囲が大きく偏る場合にはあまり効果を発揮しません。しかしながら、データを範囲ごとに区切って保存しているため、範囲指定のWHEREには強いといえます。例えば、
SELECT * FROM persons WHERE birth >= '1945-01-01' AND birth <= '1974-12-31';
 このようなクエリであれば、1945年から1974年のデータが必要なのですから、y1960とy1980の2つのパーティションのみを見るのみで足り、他のパーティションを検索する必要がないため、高速化が期待できます。
 それから、LISTパーティションなるものもあります。これは「この値の場合にはこのパーティションに所属」などといった分類をこちらで指定するものです。
 次のようなテーブルがあるとしましょう。
CREATE TABLE country(id INT , name VARCHAR(32) , PRIMARY KEY(id));

INSERT INTO country VALUES(1 , 'Canada') , (2 , 'China') ,
(3 , 'England') , (4 , 'France') , (5 , 'Japan') ,
(6 , 'Russia') , (7 , 'Spain') , (8 , 'USA');
 ちなみにアルファベット順です。
 さて、上記の国データにはアジア、ヨーロッパ、アメリカの3地方の国が含まれているのですが、ここに国データを持つ何らかのデータがあるとして、これを地方別にパーティショニングしたいとしましょう。
CREATE TABLE cities(id INT , country INT , name VARCHAR(32))
PARTITION BY LIST(country) (
PARTITION asia VALUES IN(2 , 5 , 6) ,
PARTITION europe VALUES IN(3 , 4 , 7) ,
PARTITION north_america VALUES IN(1 , 8));
 これで地方別にパーティションが作成されます。
INSERT INTO cities VALUES(1 , 1 , 'Ottawa') , (2 , 2 , 'Hongkong') ,
(3 , 3 , 'London') , (4 , 4 , 'Paris') , (5 , 4 , 'Bordeaux') ,
(6 , 5 , 'Kyoto') , (7 , 5 , 'Hiroshima') , (8 , 6 , 'Moscva') ,
(9 , 7 , 'Madrid') , (10 , 8 , 'New York') , (11 , 8 , 'Los Angeles');

SELECT * FROM cities;
2	2	Hongkong
6	5	Kyoto
7	5	Hiroshima
8	6	Moscva
3	3	London
4	4	Paris
5	4	Bordeaux
9	7	Madrid
1	1	Ottawa
10	8	New York
11	8	Los Angeles
 地方別になっていることから、どうやらパーティションが正しく動作しているらしいことが分かります。
 RANGE同様、LISTもDROP PARTITIONでパーティションを削除できます。また、リスト中にない値を指定してデータを登録しようとすると、エラーが発生します。
INSERT INTO cities VALUES(12 , 9 , 'Lisbon');

ERROR 1526 (HY000): Table has no partition for value 9
 INSERT IGNORE文を使用すると、もしデータを登録できなくてもエラーにはなりません。
INSERT IGNORE INTO cities VALUES(12 , 9 , 'Lisbon') ,
(13 , 5 , 'Nagasaki') , (14 , 10 , 'Mexico City') , (15 , 8 , 'Indiana');

Query OK, 2 rows affected (0.12 sec)
Records: 4  Duplicates: 2  Warnings: 0

SELECT * FROM cities WHERE id >= 12;

13	5	Nagasaki
15	8	Indiana
 LISTを使用すべき状況は多くはなさそうですが、知っておいて損はないでしょう。
 MySQLに用意されている、最後のPARTITIONはKEYです。これはMD5アルゴリズムでHASHを生成するものであるらしく、HASHと違って文字列なども指定できます。
 範囲指定ありのWHEREなどといった特殊な使用方法を考えないのであれば、HASHは大抵の場合に適切で簡単なパーティションの方法です。データが連番で配置されていれば、各パーティションに均等にレコードが登録されるため、検索コストはほぼ平均化されます。通常のパーティションにおいては、これは理想的な状態です。
 しかし、データは必ずしも連番であるとは限りません。AUTO_INCREMENTを使用するならまだしも、実際のデータはどのように登録されるか分かりませんし、必然的に値が偏るようなデータもあるでしょう。1つのパーティションに大半のデータが登録されてしまうのでは、パーティションを使う意味がありません。
 そこで、パーティションに使用するハッシュコードをMD5で作成し、確率論的に平均的な分布を提供するのがKEY指定です。MD5に変換して分類するため、文字列なども使用できるメリットがあります。
CREATE TABLE langs(lang VARCHAR(32))
PARTITION BY KEY(lang) PARTITIONS 2;
 それでは、適当にデータを登録してみましょう。
INSERT INTO langs VALUES('C') , ('COBOL') , ('Erlang') ,
('F#') , ('Objective-C') , ('Smalltalk') , ('Visual Basic');

SELECT * FROM langs;

C
Objective-C
Smalltalk
COBOL
Erlang
F#
Visual Basic
 無秩序な順番のようですが、実は登録順に並んでいることが分かります。パーティションの境目はSmalltalkとCOBOLの間で、その上下でそれぞれ登録順に並んでいるのです。
 KEYはMD5で値を生成しますので、連番データをHASHに使用した場合ほどパーティションごとのレコードは均等にはなりませんが、連番ではないいかなるデータを使用したとしても、高い確率で均等に近くなります。偏りのあるデータや文字列を登録するのには便利でしょう。一方、確率論的には均等になりやすいとはいえ、ある程度の偏りの存在は否定できませんし、MD5への変換その他にも多少なりともオーバーヘッドがあることを考えると、確実に連番であればHASHの方が向いているといえるでしょう。連番であってもDELETEされる場合があるようなら、HASHでも確実に均等な状態を維持できるとは限りませんが、偏ったDELETEがなされない限りにおいては、おおむね均等性は確保されますので、この場合もHASHで問題はないでしょう。
 テーブルに大量のデータが保存されている場合には、PARTITIONにはかなりの効果を期待できそうです。
カテゴリ [開発魔法][社会問題][経済・知的財産] [トラックバック 0][コメント 0]

<-前のページ [1] [2] [3] [4] [5] [6] [7] 次のページ->
- Blog by yamicha.com -