最終日になりましたのでカレンダーを完成させるためにも担当させて頂きます。できるだけ他の方の言葉でプロデルのことを話して貰えるのが良いかと思い、様子を見ていたのですが...
##プロデルで色々書いてみた
さて、今日は、昔プロデルで書いたプログラムをアラカルト的に載せていきたいと思います。
実は書きたいネタの構想はいくつかあったのですが、いざ書くとなると十分に推敲できず不時着してしまいそうなのでそれは別の機会して、今回は昔書いたプログラムを掘り起こして記事にしたいと思います。
過去に、大学の研究室の後輩と一緒にプロデルで様々なアルゴリズムやデザインパターンを書いてみるというチャレンジをしたことがありました。
当時はプロデルを作り始めた頃で言語仕様も十分でなかったので、言語仕様を整備しつつ、プログラムを作ってもらったように思います。
2019年のプロデル Advent Calendarでは、コンピューターサイエンスの話題が多くて、今回の雰囲気に合いそうなので、当時作られたプログラムの中からいくつか手直ししたものを取り上げて、プロデルや日本語プログラミングならではの事を話していければと思います。
##1.デザインパターン(Chain of Responsibility)
いわゆるGoF本のデザインパターンのいくつかをプロデルで実装してみました。その一つの「Chain of Responsibility」のプログラムを書きました。
今のプロデルでは、オブジェクト指向言語に必要な要素である、カプセル化,継承,多態性についてはおおよそ実現できていると思います。
この例のように、オブジェクト指向言語を前提とした題材は、プロデルで書いたときに思いのほかしっくり書けて読める気がします。
日本語で名付ける時の話で書き忘れていましたが、プログラムを見ると「トラブル」種類の「トラブル番号」という名前がプライベート変数と設定項目名が競合してしまっています。そのためプライベート変数を「トラブル番号X」という名前にしています。この点を解決するよい命名規則も必要です。
アリスという失敗者(「アリス」)を作る
ボブという限界番号解決者(「ボブ」、100)を作る
チャーリーという指定番号解決者(「チャーリー」、 429)を作る
ディアナという限界番号解決者(「ディアナ」、200)を作る
エルモという奇数番号解決者(「エルモ」)を作る
フレッドという限界番号解決者(「フレッド」、300)を作る
アリスがボブを頼る
ボブがチャーリーを頼る
チャーリーがディアナを頼る
ディアナがエルモを頼る
エルモがフレッドを頼る
【現在の値】を0から33ずつ増やしながら500まで繰り返す
Aというトラブル(現在の値)を作る
アリスがAを回避する
繰り返し終わり
トラブルとは
-トラブル番号X
+はじめ(数字)の手順
トラブル番号Xは、数字
終わり
+トラブル番号を取得する手順
トラブル番号Xを返す
終わり
+[自分]が文字列を表示する手順:文字列
「トラブル[トラブル番号X]番」を返す
終わり
終わり
解決者とは
+名前
受取者という解決者を持つ
受取者は、無
+はじめ(なまえ)の手順
名前は、なまえ
終わり
+[自分]が[ネクスト]を頼る手順:解決者
受取者は、ネクスト
受取者を返す
終わり
+[自分]が[問題]を回避する手順
もし[問題を判定した]なら
[問題]を解決する
他でもし受取者が、無でないなら
[受取者]:[問題]を回避する
他なら
[問題]を失敗する
もし終わり
終わり
[問題]を判定する手順:真偽値
終わり
[問題]を解決する手順
Aは、問題が文字列を表示したもの
「[A]は[名前]によって解決されました」を報告する
終わり
[問題]を失敗する手順
Aは、問題が文字列を表示したもの
「[A]は解決されませんでした」を報告する
終わり
終わり
失敗者とは
解決者を継承する
+はじめ(なまえ)の手順=(なまえ)
終わり
[問題]を判定する手順
×を返す
終わり
終わり
限界番号解決者とは
解決者を継承する
-限界番号
+はじめ(なまえ、すうじ)の手順=(なまえ)
限界番号は、すうじ
終わり
[問題]を判定する手順
もし問題のトラブル番号が限界番号より小さいなら
○を返す
でなければ
×を返す
もし終わり
終わり
終わり
奇数番号解決者とは
解決者を継承する
はじめ(なまえ)の手順=(なまえ)
終わり
[問題]を判定する手順
値は、問題のトラブル番号を2で割った余り
もし値が1なら
○を返す
でなければ
×を返す
もし終わり
終わり
終わり
指定番号解決者とは
解決者を継承する
-指定番号
+はじめ(なまえ、すうじ)の手順=(なまえ)
指定番号は、すうじ
終わり
[問題]を判定する手順
もし問題のトラブル番号が指定番号なら
○を返す
でなければ
×を返す
もし終わり
終わり
終わり
2.ケブンッリジ ジネェーレタ
当時話題になっていた「ケリブンッジ大学のコピペ」などと呼ばれている「語の中の文字の順序を変えても読める」文を生成するプログラムです。
生成文字列を作る際に、JavaのStringWriter(C#ではStringBuilder)と同様な機能を持つ「文字列書込器」という種類を使っています。英単語でerと付くクラス名にプロデルでは「器」という接尾語を付けてみました。例えばlexical analyzerを「字句解析器」、parserを「構文解析器」と呼ぶのと同じ名付けルールです。
文字列処理については操作内容の割に少々長いプログラムとなってしまうのが難点ですが、月日が経って改めて読んでも何をしているかはすぐ理解できそうな気がします。
プログラム掲示板ではシンタックスハイライトが利用できるためこちらの方が読みやすいと思います。
https://rdr.utopiat.net/cgi/progbbs/forump.php?mode=viewcode&id=13&rtn=13
文章は、「こんにちは みなさん おげんき ですか? わたしは げんき です。
この ぶんしょう は いぎりす の ケンブリッジ だいがく の けんきゅう の けっか
にんげん は もじ を にんしき する とき その さしいょ と さいご の もじさえ あっていれば
じゅんばん は めちゃくちゃ でも ちゃんと よめる という けんきゅう に もとづいて
わざと もじの じゅんばん を いれかえて あります。
どうです? ちゃんと よめちゃう でしょ?
ちゃんと よめたら はんのう よろしく」
結果書込器は、文字列書込器を作ったもの
文章を改行で区切ったもののすべての行についてそれぞれ繰り返す
行を「 」で区切ったもののすべての単語についてそれぞれ繰り返す
文字数は、単語の文字数
もし単語が「。」で終わるまたは単語が「?」で終わるなら、文字数を減らす。
もし文字数が3より大きいなら
置換後は、単語
(置換後が単語)の間繰り返す
2から文字数-2まで値を増やしながら繰り返す
値は、値から(文字数-1)までの乱数
字は、置換後の値文字目
置換後は、(置換後から先頭から値-1文字取り出す)&(置換後の(値+1)文字目以降)
置換後は、(置換後から先頭から(文字数-2)文字取り出す)&字&(置換後から末尾から1文字取り出す)
繰り返し終わり
繰り返し終わり
そうでなければ
置換後は、単語
もし終わり
結果書込器へ「 」を加える
結果書込器へ置換後を加える
繰り返し終わり
結果書込器へ[改行]を加える
繰り返し終わり
結果書込器の内容を表示する
3.Brainf*ck
難解プログラミング言語をプロデルで実装してみました。
アルゴリズム系のプログラムは、配列を多用するなどするため日本語プログラミングで書くと読みにくくなるため、不向きだと考えていましたが、コマンドのスタック操作を「スタック」種類を使って書くだけで、読みやすくなったのではないでしょうか。
「+++++++++[[]>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.>-.------------.<++++++++.--------.+++.------.--------.>+.」をブレインファックする
【ソースコード】を、ブレインファックする手順
ループスタックというスタックを作る
メモリ領域は、{}
ポインタは、1
1からソースコードの文字数まで読み取り位置を増やしながら繰り返す
命令は、ソースコードの(読み取り位置)文字目
//命令を報告
命令について分岐
「>」の場合
ポインタを増やす
「<」の場合
ポインタを減らす
「+」の場合
メモリ領域(ポインタ)を増やす
「-」の場合
メモリ領域(ポインタ)を減らす
「.」の場合
((メモリ領域(ポインタ))のキャラクタ)を改行せず報告する
「[[]」の場合
ループスタックへ読み取り位置を積む
もしメモリ領域(ポインタ)が0なら
作業位置は、読み取り位置
ブロック数は、1
(ブロック数が0より大きい)の間、繰り返す
作業位置を増やす
文字は、ソースコードの(作業位置)文字目
もし文字が「[[]」なら
ブロック数を増やす
他でもし文字が「]」なら
ブロック数を減らす
もし終わり
繰り返し終わり
読み取り位置は、作業位置
もし終わり
「[]]」の場合
読み取り位置は、(ループスタックから取り出したもの)-1
分岐終わり
繰り返し終わり
終わり
4.ライフゲーム
細胞の生き死にをシミュレーションするライフゲームです。
ライフゲームそのものだけであれば、もっと短く書けるはずですが、あえてオブジェクト指向的にプログラムを書いています。
冒頭の世界を初期空間で創造する
という文が気に入っています。この文は、「初期空間」変数を引数にして「世界」種類の「想像する」手順を呼び出す文になっています。
「広義の日本語プログラミング」では、こういった文を単一のメソッド名として定義しているのを見受けますが、プロデルでは語の一つ一つが識別子であり意味を持っています。このような文を書けるのがプロデルでプログラムを書いていて、気持ちが良い所です。
このプログラムでは、文字列で細胞を描画していますが、別にキャンバスに描画して細胞を自由に設定できるバージョンもあったかと思います。すぐに見つからなかったのでまたの機会にご紹介します。
初期空間は、{
{「 」,「 」,「 」,「 」,「 」,「 」,「 」,「 」,「 」,「 」},
{「 」,「 」,「 」,「 」,「 」,「 」,「 」,「 」,「 」,「 」},
{「 」,「 」,「 」,「 」,「 」,「 」,「 」,「 」,「 」,「 」},
{「 」,「■」,「 」,「 」,「 」,「 」,「■」,「 」,「 」,「 」},
{「■」,「 」,「 」,「 」,「 」,「■」,「 」,「 」,「 」,「 」},
{「■」,「■」,「■」,「 」,「 」,「■」,「■」,「■」,「 」,「 」},
{「 」,「 」,「 」,「 」,「 」,「 」,「 」,「 」,「 」,「 」},
{「 」,「 」,「 」,「 」,「 」,「 」,「 」,「 」,「 」,「 」},
{「 」,「 」,「 」,「 」,「 」,「 」,「 」,「 」,「 」,「 」},
{「 」,「 」,「 」,「 」,「 」,「 」,「 」,「 」,「 」,「 」}
}
世界を初期空間で創造する
メイン画面を表示する
待機する
世界とは
+マップ
[自分]を、[空間]で、創造する手順
マップは、{}
縦を1から[空間の個数]まで増やしながら繰り返す
マップ(縦)は、{}
横を1から(空間(縦)の個数)まで増やしながら繰り返す
Aという区画(横,縦,世界)を作る
もし空間(縦)(横)が「■」なら
Aの生命存在は、○
そうでなければ
Aの生命存在は、×
もし終わり
マップ(縦)(横)は、A
繰り返し終わり
繰り返し終わり
終わり
[自分]を、次へ、進歩させる手順
進歩空間は、{}
縦を1からマップの個数まで増やしながら繰り返す
進歩空間(縦)は、{}
横を1から[マップ(縦)の個数]まで増やしながら繰り返す
個数は、マップ(縦)(横)から周囲生命個体数を取得する
Xという区画(横,縦,世界)を作る
もしマップ(縦)(横)の生命存在なら
もし個数が3より大きいなら //過密
Xの生命存在は、×
他でもし個数が2より小さいなら //過疎
Xの生命存在は、×
他なら //生存
Xの生命存在は、マップ(縦)(横)の生命存在
もし終わり
そうでなければ
もし個数が3なら //誕生
Xの生命存在は、○
他なら //生存
Xの生命存在は、×
もし終わり
もし終わり
進歩空間(縦)(横)は、X
繰り返し終わり
繰り返し終わり
マップは、進歩空間
終わり
[自分]を、表示する手順
取得したものを報告する
終わり
[自分]を、取得する手順
結果は、「」
縦を1からマップの個数まで増やしながら繰り返す
横を1から[マップ(縦)の個数]まで増やしながら繰り返す
もしマップ(縦)(横)の生命存在なら
結果は、結果&「■」
そうでなければ
結果は、結果&「 」
もし終わり
繰り返し終わり
結果は、結果&改行
繰り返し終わり
結果を返す
終わり
終わり
区画とは
+横位置
+縦位置
+生命存在
+世界
はじめ(X,Y,世界)の手順
横位置=X
縦位置=Y
自分の世界は、世界
終わり
【自分】から、周囲生命個体数を、取得する手順
【個体数】は、0
縦を(縦位置-1)から(縦位置+1)まで増やしながら繰り返す
もし縦が0以下または、縦が世界のマップの個数より大きいなら、繰り返しを続ける
横を横位置-1から横位置+1まで増やしながら繰り返す
もし縦が縦位置かつ横が横位置なら
他でもし横が0以下または横が世界のマップ(縦)の個数より大きいなら
他でもし世界のマップ(縦)(横)の生命存在なら
個体数を増やす
もし終わり
繰り返し終わり
繰り返し終わり
個体数を返す。
終わり
終わり
メイン画面とは
ウィンドウを継承する
はじめ手順
初期化する
テキスト領域1の内容は、世界を取得したもの
終わり
初期化する手順
//この手順は自動生成されたものです
//編集しないでください
この内部領域大きさを{284,262}に変える
この内容を「メイン画面」に変える
このドラッグドロップを○に変える
この位置と大きさを{15,15,300,300}に変える
ボタン1というボタンを作る
その内容を「進歩」に変える
その移動順を1に変える
その位置と大きさを{104,210,75,23}に変える
テキスト領域1というテキスト領域を作る
その移動順を2に変える
その位置と大きさを{45,13,187,172}に変える
終わり
ボタン1がクリックされた時の手順
世界を次へ進歩させたもの
テキスト領域1の内容は、世界を取得したもの
終わり
終わり
#プロデルの魅力
コードゴルフや競技プログラミングが行われているプログラミングの界隈では、「いかに早く短く書けるか」ということが好まれると思います。日本語プログラミング言語を初めて見た人から一言目に「入力が面倒」と言われてしまうのは、日本語プログラミングが「早く短く書ける」ことからは逆を向いていると思われているからかも知れません。
そうした中で日本語プログラミング言語の魅力を説明するのは大変ですが、今回まとめていて気が付いたことがあります。プロデルでの日本語プログラミングの魅力は、プログラムを書いて読み返したときに、一つ一つの語に意味があり、それがすんなり意味を持つプログラム文として読めてしまうことに快感があるということです。
シンタックスハイライトされたプロデルのプログラムを見ると、一つ一つの語が別々に色分けされているのが分かります。プロデルのプログラム中の単語(字句)には一つ一つプログラミング言語としての意味があり、無駄な字句はありません。無駄そうに見えて無駄がないのです。
このような気持ちの良いプログラムをプロデルで書くためには、多少遠回りなクラスやメソッドを定義したりして、場合によってはパフォーマンスが悪くなることもあるかも知れません。ただ今のPCの資源を考えれば、さほど問題にならないはずです。
プロデルのプログラムを書くことでこうした魅力を探して紹介していくことが大切だと思いました。
#まとめ
プログラミング言語の世界で定番のプログラムをプロデルで書いてみました。
プロデルで普通に書いたつもりのプログラムでも他の人が見たら何か気が付くことがあるかと思います。また、うまく行かなくて完成しなくても、どこまでできたのかが書き記してあるだけでも大きな意味があると思います。プロデルユーザのみなさまにも、Qiitaやブログ、SNSなどでプロデルのこと、話してもらえたら嬉しいです。
#終わりに
長年プロデルを開発してきて、掲示板で質問や不具合報告を頂くものの、実際にプロデルがどのように使われているのか、使えるのかを知ったり話す機会ができずにいました。Advent Calendarを通じて、思いもしなかった使い方を知り、また可能性を感じることができました。
そして何とか、Advent Calendarを埋めることができてよかったです。
25日間毎日プロデルが語られるという特別な日々を過ごすことができ嬉しかったです。
カレンダーを立ち上げて頂いたnipo様, 記事を投稿して下さった雪下様, 電脳福祉士様、また記事をご覧頂いた方に感謝致します。
メリークリスマス
そして、よいお年を!