[Piet作ってみた]円周率の近似値表示プログラムの解説

こんにちは。先日1年半ぶりに投稿して、円周率の近似値を表示するプログラムを作ってみました。

ymos-hobby-programing.hatenablog.com

今回はこのプログラムの仕組みを解説をしていきたいと思います。

ソースコードおさらい

円周率の近似値を表示するプログラムをもう一度見てみます。

円周率の近似値を計算する海岸風景

メッシュが荒すぎて海岸風景に見えないところはご愛嬌です。

作り方おさらい

このプログラムはPietソースコードを、海岸風景の写真(をPiet風にリメイクしたもの)に埋め込むことで作られています。埋め込む際には、円周率プログラムの処理部分と、ただの背景部分がごちゃ混ぜにならないようにする必要があるため、人手でやるのは至難の技です。

そこで、

  1. Pietソースコードの生成
  2. 背景画像へのPietソースコード埋め込み

を自動的に行うプログラムを作っています。

github.com github.com

今回は、背景として次のような海岸風景の画像を使い、

背景の元画像

これに円周率の近似値計算と表示処理をするPietソースコードを自動生成して埋め込んでいます。

円周率の近似値計算と表示処理をするPietソースコード

興味があればgithubからソースを落として遊んでみてください。詳細は過去記事でも説明しています。

ymos-hobby-programing.hatenablog.com

ymos-hobby-programing.hatenablog.com

処理の概要

円周率の近似といっても、大したことはしていません。355/113という分数が円周率の近似値として使われるそうなので、単純に355÷113という割り算の結果を表示しただけです。なんてことはない。

処理の流れ

とはいえPietでは整数しか扱えませんので、小学校で教わる割り算の筆算を、自力でPietプログラムに落とし込むことになります。 割り算の筆算の仕方、覚えてますか?電卓信者の私は思い出すのに数分かかりました。

割り算の筆算は次のようにするとおもいます。

  1. 割られる数(355)の左隣に割る数(113)を書き、その間に”)”を書き、割られる数の上に横線を引く
  2. 商(=割られる数の中に割る数がいくつ入っているか)を計算する
    (割る数113の3倍が339、4倍が452なので、商は3)
  3. 割られる数の棒の上、355の右端の5の上に、商”3”を書く(この時だけ小数点を打つ)
  4. 割る数113に商3をかけた339を、割られる数355の下に書き、引き算する(答えは16)
  5. 引き算の答え16の末尾に0をつけた160を、新たに割られる数とみなす
  6. 商(=割られる数の中に割る数がいくつ入っているか)を計算する
    (割る数113の1倍が113、2倍が226なので、商は1です)
  7. 割られる数の棒の上、先ほどの商”3”の右隣に、次の商”1”を書く
  8. 割る数113に商1をかけた113を、割られる数160の下に書き、引き算する(答えは47)
  9. 引き算の答え47の末尾に0をつけた470を、新たに割られる数とみなす
  10. 以下繰り返す

よくみると6は2と同じ処理であり、そのほかも同じ処理を繰り返しています。 また、5などに出てくる「末尾に0をつける」とは、「10倍する」のと同じことです。 これらを意識して処理の流れを書き直すと次のようになります。

  1. 割られる数(最初は355)と割る数(最初は113)を準備する
  2. 割られる数を割る数で割った商(整数)を求める
  3. 商を表示する(最初だけ小数点を表示する)
  4. 割られる数から「割る数×商」を引き算する
  5. 引き算の結果(=余り)を10倍したものを新たな割られる数とする
  6. 2に戻る

今回作ったPietプログラムの処理の流れもおおよそ上の通りです。 ただ、このままだと無限ループするので、 小数以下指定した桁まで表示したら処理を止めるようにします。

Pietソースコード生成

それではまずPietソースコードを自動生成します。 自動生成のためには、処理の流れを専用のプログラミング言語で記述する必要があります。

専用の言語についても過去記事で紹介はしています。Pietの命令を文字起こしして処理順序を書いたものになりますが、難易度はやや高めです。

ymos-hobby-programing.hatenablog.com

ymos-hobby-programing.hatenablog.com

この言語で割り算の筆算処理を記述すると次のようになります。

#割られる数の初期値(355)入力[処理1]
push5 dup push7 mul push2 mul push1 add mul
#割る数の初期値(113)入力[処理1]
:bumbo push3 push2 push1 push2 dup mul add dup dup push1 add add mul mul add
# ループ回数(10回)と小数点を打つかどうかのフラグを初期化
:shori push1 push10 push4 push2 roll
#---ループ開始---
# 計算に使う数値をメモリ上で複製
:dispfracloop dup dup push4 push3 roll dup push4 push3 roll
#商の計算と出力[処理2〜3]
div outn push2 push1 roll
#余りの計算[処理4〜5]
mod push2 push5 mul mul push2 push1 roll
#小数点表示判定[処理3]
push4 push3 roll dup if::pccheck
#小数点表示(初回のみ)
push1 sub push7 push7 mul push3 sub outc 
# ループ終了判定
:pccheck push4 push1 roll push3 push2 roll push1 sub dup if::end
# ループ継続処理[処理6]
push3 push1 roll goto:dispfracloop
##---ループ終了---
# 終了時の割られる数と割る数を表示(デバッグ)
:end push4 push8 mul outc pop outn push4 push8 mul outc outn pop end

このファイルをPietソースコード自動生成プログラムに入力すると、先ほど示したPietソースコード画像が出力されますが、どこでどのような処理をしているのかパッとみただけでは分かりません。

Pietソースコードの解析

Pietソースコードの自動生成プログラムのログを解読すると、どこでどのような処理をしているか分かります。それを図にしてみると次のようになります。

ソースコード図解

プログラムは左上の「割られる数の初期化」から始まって、図中の矢印の順に処理内容が変化していき、最終的に右上の「デバッグ表示」に到達して終了します。途中、赤矢印で示した部分はループ処理です。赤点線は、初回のみ通るルートで、小数点を表示しています。

ちなみに、自動生成されたPietソースコードは、画像フチで処理を行い、中心部分はほとんどソースコードがありません。そのため、画像にソースコードを埋め込んでも、元の画像が崩れにくくなります。

一方で、画像中央には細かい色付きセルが散らばっています。これはPietインタプリタの移動方向を制御するためのもので、上図の矢印の順に処理が進むように配置されています。その結果、Pietインタプリタは画像のフチだけでなく、画像全体を縦横無尽に移動することになります。

実行結果

このPietソースコードを実行すると、円周率の近似値355/113=3.141592920と、ループ終了時の割る数113と割られる数400を表示して終了します。

3.141592920 113 400

最後に

前回紹介した円周率表示プログラムの解説をしました。やっぱり解説するとなると、それなりにブログ書くのに時間かかりますね。

さて、ふとPietで暗号化プログラムが作れないかと思い立ちまして、先日から試しておりました。結果、なんとかうまくいったので次回以降紹介したいと思います。暗号といっても古典的(?)なもので、カエサル暗号やビジュネル暗号などのことです。それぞれの暗号がどのようなものか解説もしながら、次回以降、説明していきたいと思います。

ではまた。