2014年12月15日月曜日

CodeIQ 第7回デスマコロシアムの解(Pascal でのショートコーディング)

Delphi / Appmethod Advent Calendar 2014 12/15 の記事です

CodeIQ というリクルートのサイトで、@tbpgr さんによって不定期開催されている「デスマコロシアム」というコードゴルフ(短いコードを競うもの)大会があります。
このデスマコロシアムは ideone で採点されるので、なんと! Free Pascal(fpc)が言語に入っているのです。
CodeIQ で Pascal が使えるのは非常に珍しい…

ちなみに fpc は Object Pascal というか Delphi 互換(を目指している)なので、Delphi を持っている場合は ideone よりも簡単に try & error で色々チャレンジできます。
しかし!Pascal はとても解りやすく書けるためショートコーディングには向きません!
/(^o^)\
まあ、そんなこと言っても始まらないので、ショートコーディングのポイントをいくつか紹介します。

デスマコロシアムの問題

今回の問題は…
下記の文字列を標準出力せよ

>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.>++++++++++++++++++++++++++++++++.>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.
でした。
解る人には解りますが、これは brainf*ck のコードです。
なので、これを brainf*uck で走らせると
deathma colosseum
という文字列を出力するコードだと解ります。
なので、これは
">"+(文字コード分の"+")+"."
を出力すればいいという事になります。

より詳しい解説はオフィシャルの CodeIQ MAGAZIN をご覧ください。

解答

僕の解答は次の通り。
fpc で 77 文字に纏まりました。
そのコードがこちら。
for PChar(@argc)^in'deathma colosseum'do Write(^~,StringOfChar('+',argc),'.')

0.文字の字詰め


これは当然ですが出来る限り字を詰めて書きます。
文字列のシングルクオートの後は空白をいれずに予約語を書けたり、ポインタ逆参照演算子でも予約語を書けます。
他にも整数などの数についてもできます。
これは前提条件なので0番です。

1.for-in-do


fpc なので for-in-do 文が使えます(GNU Pascal や Pure Pascal では使えません)。
コードにあるように即値で文字列を書けるので、それだけでも短くなります。
ただ、1つ注意が必要なのは Delphi では、PChar(@argc)^ とは書けません!
Delphi のコンパイラの方が厳格なので E1019 が発生します。
Delphi 使うと簡単に書けるよ!っていっておきながらコレ!

2.定義済み変数


Delphi では System.pas などに定義済み変数がいくつか定義されています。
それと同じように fpc にも定義済み変数があります。
使い出がありそうなのは…

変数名 本来の意味
argc Integer 引数の数
argv PPChar 引数へのポインタ(256 byte 確保済み)
envp PPChar 環境ブロックへのポインタ(256 byte 確保済み)

これらを使うと var ブロックを書く必要がありません!
ただ変数名が長いので変数がたくさん出てくる場合は var ブロックを書いた方が短くなるかもしれません。

3.コントロールコードの記述


Pascal にはコントロールコードを直接記述する記法が存在します。
プレフィクスとして "^" を付けると、その後に続く文字の ASCII コードから $40 を引いたものが、その値となります。
たとえば ^A とすると #$01 が返ります。
現在の実装だと文字コードから単純に $40 引くだけなので、上手くいけば 1byte コードを短くできます。
今回は運良く ">" が "~" の $40 前だったのでコレを使いました。

4.標準関数の使用


fpc では System.pas に StringOfChar が定義されているので、そのまま使えます。
また、System.pas に定義されていないもの、他のユニットに定義されているものも使えます。
他のコードゴルフでは解りませんが、デスマコロシアムでは include, import はコードの文字数に入らないため、uses も文字数に入りません!
なので、実は uses StrUtils; としておけば、StringOfChar より2文字短い DupeString が使えます。
これを忘れて、そのまま出してしまいましたが…

5.型名


今回は var ブロックを使いませんでしたが、使う場合は型名に気をつけます。
たとえば Integer は7文字ですが、Byte, Word は4文字です。
このように型名だけで3文字も変わるので、必要なとき以外は Integer を使わないようにします。

6.関数名への代入


今回は関数・手続きを使いませんでしたが、使う場合は戻り値の返し方にもテクニックがあります。
元々 Pascal の関数が値を返す方法は Result 変数ではなく、関数名に戻り値を代入する方法でした。
ただ、これだと再帰関数を作るのが難しかったため、Result 変数が Turbo Pascal で導入されました。
ですが、関数名に戻り値を代入するのは、未だに有効な書き方です。
これを使うと…
function p: Byte;
begin
p := 1;
end

このように書けるため Result を使うよりも短く書けます。

7.文字列の取り出し方


Delphi だと次のように文字列の一部を取り出せます。

Writeln('deathma colosseum'[1]);

ですが fpc では、このコードはエラーになります。
では、文字列をいったん const や var に入れないと使えないのか、というと、そうでもありません。
fpc では、こんな風にするとコンパイルが通ります。

Writeln(('deathma colosseum')[1]);

8.Delphi だけの技

Delphi では、function / procedure をクロージャとしていきなり書けます。
括弧の使い方の妙技ですね! これを使うとコードを短く出来る可能性があります。

Writeln((function:String begin Result:='deathma colosseum'end)());

もう1つ class / record helper が使えます。
fpc も将来的に使えるようになるっぽいです。

GNU Pascal での解

GNU Pascal (gpc) での解も載せて起きます。

var i,j:Byte;
for i:=1to 17do begin Write('>');for j:=1to Ord(('deathma colosseum')[i])do Write('+');Write('.');end
デスマコロシアムでは元々書いてある部分(program ideone; begin end.)は文字数に入りません。
なので、上記のコードも、それらは省いてあります。
gpc 版でも上記の5番と7番のテクニックを使っています。

まとめ

と、僕が知る限りのショートコーディングのテクニックを紹介しました!
コードゴルフで Pascal が躍進するきっかけになればいいなと思っています!

ちなみに、現在(2014/12/15)デスマコロシアム第8回が開催されています。
腕に自信のある方々は、ご参加されてみたらいかがでしょう?
多くの言語があるので、Pascal 以外でも楽しいと思いますよ!

0 件のコメント:

コメントを投稿