Hatena::ブログ(Diary)

Qu記(仮) RSSフィード

2011年 03月 20日

Herbert Tutorial 3 - 命令引数プロシージャ / n倍処理

前回はプロシージャの概念を学習して、「くどさ」を発見することでコードを簡潔化・短縮する技術を身に付けました。

今回は、プロシージャの重要な機能である「引数」を導入することで、さらに「くどさ」を一般化して捉え、論理的に高度で面白いプログラムの記述を目指します。


練習問題2の解法

今回も、前回の演習問題の解き方を確認するところから始めましょう。

前回の演習問題は http://herbert.tealang.info/problem.php?id=199 でしたね。

まだ取り組んでいない人は、答えを見る前に自分で解いてみましょう。

それでは、下に想定解法を示します。

f:id:quolc:20110320144132p:image

(コード)

a:srsl

b:aaaar

bbbb

ギザギザ走行の最小単位を a という名前のプロシージャにまとめ、4回分のギザギザ走行をおこなってから右折する、という一連の動きを b という名前のプロシージャにまとめる。

そして、そのbを4回連続で実行することで、ひし形にHerbertを歩かせたというわけです。

このコードによって、制限byte数15きっかりにクリアすることが出来ました。


13bytes!?

前々回の演習問題では、適切なプロシージャを使うことでリミットの20bytesよりも5bytes短縮した15bytesの解答を見つけることが出来ました。

今回もランキングを見てみると、何やら先ほどの解法の15bytesよりも、2bytes少ない13bytesという解答を示している人がたくさんいますね…。

彼らは一体どうやってそのようなスコアを叩きだしたのでしょうか。

この疑問への答えとなる模範解法には、実は全く異なる二つのものがあります。

そのうち片一方は次回に解説を回すとして、今回はHerbertの文法上多分一番重要な事項である、「命令引数プロシージャ」を用いる解法を紹介します。


「抽象的なくどさ」の発見

それでは、前回同様再び先ほどのコードをじっくり読み返してみましょう。

そして、やはり前回同様に、「なんかくどいな」という部分を発見しましょう。

a:srsl

b:aaaar

bbbb

が先ほどのコードですが、いかがでしょうか。何か感じませんか?

多分、これは前回よりもすぐ思い当ると思いますが、次のような「くどさ」が分かると思います。

a:srsl ← これは特に問題ない

b:aaaar ← aを4回連続で実行している

bbbb ← bを4回連続で実行している

何やら、「4回連続で」同じことを行う、という部分が共通していますね!

この互いに似通った命令を1つの言葉で統一的に表せれば、コードがすっきりしそうです。


命令引数プロシージャ

そこで登場するのが、先ほどから名前を出している命令引数プロシージャです。

ひとまず命令引数プロシージャを使った簡単なコードを示してみましょう。

a(X):XXX

b:srsl

a(s)a(b)

実際にテストフィールドで一歩ずつ動かして、働きを確かめてみましょう。

「3歩」直進した後に、"srsl"の動きを「3回」連続で行いましたね。


太字の部分が、新しい文法要素です。

命令引数プロシージャの宣言は、プロシージャ名を書いた後に、カッコで挟んでXYなど、大文字のアルファベットを書くことで行います。

命令引数プロシージャのコード部分(コロンの後)では、そのXやYをプロシージャや基本コマンド(s,r,l)と同じように使うことができます。

上のコードでは、実際にXXXと書かれていますが、これはXという仮想的なプロシージャを、sを3回連続でsssと書くのと同じように、3回連続でXXXと書いたということです。

そして、定義された命令引数プロシージャは、a(s)やらa(ss)やらa(b)やらといった形で実行することができ、実行時にはXの代わりにカッコに挟んだ内容(sとかssとかbとか)が置換されて実行されます。


少しややこしいでしょうか?


命令引数プロシージャを使いこなすには、まず命令引数とは何かということをきちんと理解していないといけませんが、これは慣れていないと難しい概念かもしれません。

ここでは、Herbertプレイヤーの皆さんは中学卒業程度の英語は習得しているものとして、命令引数を「不定詞」のようなものだと考えることで説明してみたいと思います。

そして、命令引数プロシージャとは、「不定詞を目的語に取る動詞」と考えてみましょう。

例えば "forget" などが典型的でしょう。

「Xし忘れる」を、 "forget to X" と表現しますが、ここで、Xの代わりにYという動詞を突っ込んでみても、"forget to Y"となり、問題ないわけです。

Herbertの文法に合わせてみれば、 "forget to X" から "to" を省き、 "forget" を "f" と言い換えて形式的に書き換えてみると、"f(X)" という形になり、納得いくのではないでしょうか。(少し無理やりでしょうか。)

そして、「Xし忘れる」「Yし忘れる」という動詞がそれぞれバラバラではないおかげで英語を覚えるのがそこまで大変ではないように、「aaaa」「bbbb」というそれぞれの一連の命令を「f(X):XXXX」として、「f(a)」「f(b)」と統一的に表せることで、Herbertも定義が簡潔になる、というわけです。


命令引数プロシージャとはこんな風に、動詞(つまり、プロシージャやコマンド)を何でも取り込んで文章を作ることができる、不定詞を目的語に取る他動詞のような汎用的な言葉の部品なのです。

これに対して、前回解説した引数無しプロシージャや前々回に解説した基本コマンドは、目的語を取らない動詞、つまりは自動詞であると考えればいいわけですね。

なお、ここで英語の類推で考えると、「他動詞(の不定詞)をさらに目的語に取る他動詞」があってもよさそうです。

"forget to remember to X" (Xすることを覚えていることを忘れる)というような文章があってもいいわけですから。

これはHerbertでも正しく、ある命令引数プロシージャの命令引数として、命令引数プロシージャそのものを突っ込むことが可能です。

例えば

a(X):XXXX

b(X):XrX

a(b(s))

みたいな書き方もアリ、というわけです。


n倍処理

Herbertで頻要なのがn倍処理と呼ばれるイディオムで、特に3倍処理と4倍処理はしばしば使うことになります。

これの定義は簡単で

a(X):XXX ←3倍処理

b(X):XXXX ←4倍処理

これだけです。

同じことを4回行うことを、さらに4回行う、というような例はよくあります。

先ほど分析した練習問題2は、まさにそのタイプでしたね。


練習問題2の解法'

それでは、命令引数プロシージャを上手く使って、練習問題2を解きなおしてみましょう。

13bytesによる簡潔な解法を見つけることができるはずです!


練習問題3

それでは、今回のまとめとなる練習問題です。

今回は、練習問題2の解法'が分かればすぐに解けるはずです!

http://herbert.tealang.info/problem.php?id=242


また、類題として

http://herbert.tealang.info/problem.php?id=4

も解いておきましょう。


それでは今回はここまでです。

次回は別のタイプの引数を取るプロシージャについて勉強して、もう少し柔軟な動きを実現できる方法を身に付けることにしましょう。

それでは、さようなら。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/quolc/20110320/1300603197