[今日のPiet]GridPietGenerator入門編3(ループの応用1)
どもです。
GridPietGeneratorの 入門編、第3弾です。
今回はループの応用編です。
ループ処理色々
前回説明したループ処理を改造して、色々な処理をしてみましょう。
基本形は、前回紹介したこのプログラムです。
push3 dup mul #1 :loop dup if::end dup outn #2 push1 sub goto:loop :end pop end
ymos-hobby-programing.hatenablog.com
実はループの基本形は大体同じで、
つぎのように組めば大体なんとかなります(多分)。
- 上記のプログラムを書く
- 1行め(コメント「#1」の行)でスタックを初期化する
- 4行め(コメント「#2」の行)でループ内の処理をする
カウンタの2乗を出力する
まずは練習問題として「カウンタの2乗」を出力する
プログラムを書いてみます。
前回のプログラムでは、9、8、・・・、1の順に値が出力されました。
そこで今回のプログラムでは、それぞれの2乗、つまり、
81、64、・・・、1の順に値を出力させます。
さて、実はこの2乗出力プログラムは、
前回までに出てきた命令だけで書くことができます。
もし興味のある方は、先に進む前に
どうすれば2乗を出力するプログラムが作れるか考えてみてください。
では解説です。冒頭でも説明した通り、
ループは以下の3点を気にすればいいのです。
- 上記のプログラムを書く
- 1行め(コメント「#1」の行)でスタックを初期化する
- 4行め(コメント「#2」の行)でループ内の処理をする
1つめは、有限のループを作るためのテンプレみたいなものです。
(詳細は前回の解説を参照してください。)
ymos-hobby-programing.hatenablog.com
2つめは、スタックの初期化です。
複雑な処理にするほど色々な初期化が必要になりますが、
今回は前回と同様、ループの回数iをプッシュするだけでOKです。
3つめ、ループ内の処理、ここがポイントです。
この場所で、
- ループのイテレータiの2乗を作る
- を出力する
この2つを実行すれば、目的のプログラムは完成です。
結果、つぎのようなプログラムになります。
push3 dup mul #1 :loop dup if::end dup dup mul outn #2 push1 sub goto:loop :end pop end
前回のプログラムと比較すると、違うのは4行め、
コメント「#2」の行のみ変化しています。
この行でのスタックの変化はつぎの通りです。
まず、前回にも解説した通り、
4行めの最初のスタックの状態は、つぎのようになっています。
ここから、つぎの手順で命令を実行します。
この過程でスタックはつぎのように変化します。
4行め終了時点で、スタックの状態は再びつぎのようになります。
前回のプログラムの4行め終了時点でも、同じスタックの状態になっていたため、
このまま置き換えてもその後の処理には影響しないことがわかります。
Pietソースコード
Piet画像はつぎのようになります。
前回のプログラムと同じような構造になっていることがわかります。
書き換えた箇所が少しだけなので、当たり前ですが・・・。
2乗プログラムの問題点
さて、この2乗プログラムの出力をnpietで確認すると、つぎのようになります。
816449362516941
一瞬戸惑いますが、ちょっと考えれば納得できると思います。
スペースなく81、64、49、36、25、16、9、4、1とすれば、
上記のような数字の羅列が出力されることがわかります。
理解はできますが、やはり読みにくいですね。
2乗出力プログラム(出力改善ver)
先ほどのプログラムでは、出力が繋がっていて読みにくいという問題があります。
そこで、半角スペースで数値を区切りながら出力するプログラムを書いてみます。
さて、半角スペースの出力をどこに書くかですが、
やはり先ほどの3点に立ち返ります。
- ループのプログラムテンプレを書く
- 1行め(コメント「#1」の行)でスタックを初期化する
- 4行め(コメント「#2」の行)でループ内の処理をする
今回も、ループ内の処理に関わるところなので、
4行めに手を加えていけばよいだろうと予想できます。
これを踏まえて、半角スペースの区切り込みの
ループプログラムはつぎのようになります。
push3 dup mul #1 :loop dup if::end dup dup mul outn #2-1 push2 dup dup add #2-2 dup mul mul outc #2-3 push1 sub goto:loop :end pop end
ちょっと長いので、4行めを3分割して
コメントに「#2-1」、「#2-2」、「#2-3」と番号を振りました。
解説
「#2-1」は、先ほどの2乗出力のプログラムと同じです。
「#2-2」と「#2-3」を今回新たに追加しました。
「#2-1」の終了時点で、スタックの状態はつぎのようになっています。
この状態からはじめて、まずpush2命令で整数2をプッシュします。
続いて、2回dup命令を実行し、スタックに3つの数値2が積まれた状態にします。
この状態から、add命令を実行します。
add命令は、加算命令、つまり、足し算です。
つまり、スタック最上部の2つの値をポップして、最上部にあった値と、その下にあった値の和を計算し、
結果をスタックにプッシュします。
今の例だと、スタック最上部の2つの「2」をポップして、
その和を「4」をスタックに再びプッシュします。
この処理の後、スタックは次の状態になっています。
その後、#2-3の行で、dup命令を実行し、値「4」を複製します。
そして、mul命令を2回実行して、スタックに積まれている
イテレータ以外の値全てを掛け算します。
これでスタックには、イテレータiと値「32」が積まれた状態になりました。
さて、なぜ値「32」を作ったのか。
それは、半角スペースの文字コードが「32」だからです。
そして、この状態からoutc命令を実行します。
outc命令は、スタック最上部の値をポップし、ポップした値と同じ文字コードを持つ文字を出力します。
したがって、コンソール画面の出力には、
文字コード「32」の""、つまり、半角スペースが出力されます。
outc命令は実行時に値をひとつポップするので、
実行後のスタックはつぎの状態になります。
この状態は、当初の4行めの最後の状態と同じなので、
このまま処理をつぎの行に引き継ぐことができます。
Pietソースコード
では、この処理のPietコードを出力してみます。
半角スペースの文字コード32を作っているのは右辺に沿った部分です。
この過程が少々長いため、全体的に縦長のコードになっています。
また、npietの出力結果を以下に示します。
Pietインタプリタの移動ルートの形状は、これまでとあまり変わりませんね。
一部の処理がただ長くなっただけだということがわかります。
そして最後に、このプログラムの出力結果を示します。
81 64 49 36 25 16 9 4 1
数値と数値の間に半角スペースが入っていることがわかります。
これで読みやすくなりました。
おわりに
今回はループの応用方法について説明しました。
ループ内の処理を変更して、少々複雑な処理を実行しました。
また、今回は新しい命令add命令、outc命令を紹介しました。
前回までの命令と合わせると、これまでに10個の命令を紹介したことになります。
命令の挙動は複雑なので、適宜チートシートなどを見返しながら、
プログラムを組んでみてください。
次回もループの基本的な使い方を説明します。
その中で、Pietでも重要な命令の一つである
roll命令を紹介します。
roll命令をマスターできれば、より柔軟にスタックを
操作することができるようになります。
(それだけに、若干挙動が複雑ですが・・・。)
じゃ、今日はこの辺で。
では。