はじめに
少し昔の記事になりますが、こちらの記事で「関数型プログラミング」について紹介されています。
そこではお題として、「唐揚げ弁当をつまみ食いする」の実装があげられていました。ここにそれを再掲させていただくと、
課題:
唐揚げ弁当がいくつかあるとします。それぞれ唐揚げが複数入っています。
この中からx個の唐揚げをつまみ食いするプログラムを作りましょう。
つまみ食いはバレないようにするために、
その時点で最も唐揚げ数が多いお弁当から取るという仕様にします。
となります。
そして元記事では、「関数型プログラミングをする」ことの比較対象として「オブジェクト指向で解く」ことがあげられていました。
それで、その記事を読んでいた時、そういえば昔、オブジェクト指向プログラミングがよくわからなかったな〜ということを思い出しました。というのも、現在ではオブジェクト指向プログラミングの三大要素(カプセル化、継承、ポリモーフィズム)は基本的な事項として認識されていますが、当時(1990年代はじめ)はカプセル化こそが重要で、継承はどちらかというとオマケ扱いでした(と、自分には思えた)。
しかし、カプセル化をすると何が便利なのだろう、、、ということがいまいちピンと来なくて、長く疑問に感じていました。
ということで、前置きが長くなりましたが、オブジェクト指向プログラミングが流行する以前に使われていた、いわゆる手続き型(命令型)プログラミングでこの課題を解いてみたいと思います。使用言語は、懐かしのPascalです。
Pascalでの「唐揚げ弁当つまみ食いプログラム」
今回使ったのはFree Pascalなので、実はオブジェクト指向プログラミングも出来るのですが、(なるべく)標準Pascalの機能だけで実装しました。(sysutilsと文字列型は使っています)
まずは、お弁当クラスに相当するお弁当レコード
type
Bentoo = record
dish: string;
num: integer
end;
お弁当を表示する関数
procedure show(b: Bentoo);
begin
writeln( b.dish + IntToStr(b.num) + '個' );
end;
そして、pascalなので、プログラムで利用する変数をすべて宣言します。
var order : array [0..2] of Bentoo;
var eat,i,loop,maxIndex : integer;
初期データ
order[0].dish := '唐揚げ';
order[0].num := 10;
order[1].dish := '唐揚げ';
order[1].num := 8;
order[2].dish := '唐揚げ';
order[2].num := 6;
唐揚げ弁当の中で、唐揚げが一番多い弁当を探す
// 唐揚げが最大数の弁当を探す
maxIndex := 0;
for i := 0 to length(order)-1 do
begin
if (order[maxIndex].num < order[i].num) then maxIndex := i;
end;
つまみ食いをする。再代入可能なので、単に唐揚げの数をひとつ減らしています。
order[maxIndex].num := order[maxIndex].num - 1;
この「唐揚げが最大のお弁当を探す」と「つまみ食いをすること」の処理をforループで5回繰り返しています。
プログラム全体は次のようになります。
program karaage;
uses sysutils;
// レコードの定義
type
Bentoo = record
dish: string;
num: integer
end;
// レコードの表示関数
procedure show(b: Bentoo);
begin
writeln( b.dish + IntToStr(b.num) + '個' );
end;
var order : array [0..2] of Bentoo;
var eat,i,loop,maxIndex : integer;
begin
// データの準備
order[0].dish := '唐揚げ';
order[0].num := 10;
order[1].dish := '唐揚げ';
order[1].num := 8;
order[2].dish := '唐揚げ';
order[2].num := 6;
// つまみ食い実施
eat := 5;
for loop := 0 to eat-1 do
begin
// 唐揚げが最大数の弁当を探す
maxIndex := 0;
for i := 0 to length(order)-1 do
begin
if (order[maxIndex].num < order[i].num) then maxIndex := i;
end;
// 1個食べる
order[maxIndex].num := order[maxIndex].num - 1;
writeln('------');
for i := 0 to length(order)-1 do
show(order[i]);
end;
end.
ざっくり言うと、forループとif文で処理を行う典型的な手続き型(命令型)プログラムだと思います。
実行結果は、
------
唐揚げ9個
唐揚げ8個
唐揚げ6個
------
唐揚げ8個
唐揚げ8個
唐揚げ6個
------
唐揚げ7個
唐揚げ8個
唐揚げ6個
------
唐揚げ7個
唐揚げ7個
唐揚げ6個
------
唐揚げ6個
唐揚げ7個
唐揚げ6個
となりました。
感想
Pascalでも、ほぼ問題なく同様のプログラミングが出来ました。ある意味それは当然で、オブジェクト指向の特徴の1つであるカプセル化は、カプセル化という制約がない言語でも同じように実装出来ますよね。カプセル化されてないので、プログラマーが気をつけて複雑にならないようにしなくてはいけませんが。
オブジェクト指向の特徴のうち、継承やポリモーフィズムは、それがない言語では同じようには作れないと思います。でもこれも、レコードの拡張機能(Haskellのextensibleみたいな)ものをPascalに導入すれば良いだけで、オブジェクト指向にはしなくてもいいのかな、、、と思いました。
といいつつ、非常に美しいコードで好きだったPascalをひさしぶりに使ってみて、やはり良い言語だな〜と思いました。最近のオブジェクト指向言語は、JavaにしてもRubyにしても、いろんな要素を取り込みすぎて、言語仕様が複雑になってしまい、初学者には不向きな気がするので、プログラミング教育を考えるともう少し分かりやすい言語が登場して欲しいです。