沼津高専2019アドベントカレンダーの2日目を担当します、NIT2.0責任者の村尾です。
今年の高専祭では経験豊富な上級生がいない中での制作となり、開発のアドバイスをいただけない環境で展示物等を完成させましたが、これらを全校に見てもらえる機会がM4教室にとどまってしまったことを反省しています。来年度は私たちも4年生になり、企業への就職等も考える時期になりますので、何か技術力をアピールできるようなすごいものを作ります。
1日目担当のHoppyを始め、複数人で制作したARスタンプラリーも来年度は余裕をもって開発を終えて、皆さんのスマホで1度はマーカーをかざしてもらえるように努力します。
今回は特に記事にするようなことが見つからなかったので難解プログラミング言語のBrainfuckの紹介でもしようかと思います。
#Brainfuckとは
プログラミング言語の一つで、>
、<
、+
、-
、.
、,
の8つの文字だけで記述します。他の文字がソースコード内にあると全て無視されます。
そのため一つの処理で表現できる内容が少ないため、これらを複数個組み合わせてほかの言語の一つの関数と同じように動作させなければならなく、実用的な場面で使うことはかなり難しいです。一種の娯楽のためのプログラミング言語とでもいうものだと思います。
文字はASCIIコードとして数値で扱うことができるので、1文字ずつ出力させたい文字のASCIIコードの値までポインタの指す値をインクリメントをさせて出力する流れになります。
文字 | 動作 |
---|---|
> | ポインタ(のアドレス)をインクリメント。C言語なら ++ptr; |
< | ポインタ(のアドレス)をデクリメント。C言語なら --ptr; |
+ | ポインタの指す値をインクリメント。C言語なら ++(*ptr); |
- | ポインタの指す値をデクリメント。C言語なら --(*ptr); |
[ | ポインタの指す値が0なら、後の]までジャンプ (while(*ptr)) |
] | ポインタの指す値が0でなければ、前の[までジャンプ |
. | ポインタの指す値を出力 |
, | 入力から1バイト読み込んで、ポインタが指すアドレスに代入 |
#Brainfuckの特徴
- 難しい
- 他人のコードはまず読めない
- ポインタに強くなる(個人差あり)
- ASCIIコードに強くなる(個人差あり)
#環境作り
Ubuntuなどのaptコマンドが使える環境では
$ sudo apt install bf
とターミナルで入力するだけです。
Macでは
$ brew install brainfuck
と入力すればインタープリタがインストールできるそうです
#ACを出力するコード
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.++.>++++++++++.
見たところ+
がめちゃくちゃありますね。
AのASCIIコードが65なので65回値をインクリメントしています。
その後に65を文字に変換したものを出力して、アドレスをインクリメントし、10回値をインクリメントして出力しています。
ASCIIコードでの10は改行コードです。C言語でprintf("A\n");
とやるのと同じように文字列を出力した後は改行コードを出力させています。
ソースコードをファイル名.bf
で保存したら、
Linuxでは
$ bf ファイル名.bf
Macでは
$ brainfuck ファイル名.bf
で実行して、確認してみてください。
#ソースコードを短くする
ACと表示させるだけで+を70回以上も打つとなると、英文を1つ表示させるだけで+を何回書かなければならないんだってなりますよね。
そこで、[]
を用いてソースコードを短くすることができます。元々無に等しい可読性がさらに低くなることになりますが...
+++++[>+++++++++++++>++<<-]>.++.>.
これでソースコードは短くなりましたが、圧倒的に読みづらくなりましたね。
まず、アドレスを2回インクリメントいているので3バイト分のメモリを使用しています。それらをアドレス順に、a[0]、a[1]、a[2]としましょう。
最初の
+++++
の部分でa[0]を5回インクリメントしているのでa[0]は5になります。
次に、
[>+++++++++++++>++<<-]
の部分ですが、最初の[
では__a[0] = 5__なのでループに入ります。>
でアドレスをインクリメントして値を13回インクリメントしているので__a[1] = 13__になります。その後アドレスをインクリメントして値を2回インクリメントしているので__a[2] = 2__になります。その後に<<
でアドレスを2回デクリメントし、値をデクリメントしているので、アドレスはa[0]に戻り、__a[0] = 4__になります。その後、]
a[0]の値は0でないことが確認され、ループの最初に戻ります。
以上から、ループのなかではa[1]を13回、a[2]を2回インクリメントし、a[0]を1回デクリメントしています。
C言語風に書くと
while((*ptr) = 0){
ptr++;
(*ptr) += 13;
ptr++;
(*ptr) += 2;
ptr--;
ptr--;
(*ptr)--;
}
という風になります。(これが読めればポインタはばっちり!)
a[0]が5から0になるまでループし続けるので、5回ループ内の処理が実行されるようになり、a[1] = 13×5 = 65、__a[2] = 2×5 = 10__になり、それぞれ先ほどと同じようにASCIIコードのAと改行に当たります。
その後に
>.++.>.
の部分で、a[1]を出力して値を2回インクリメントした(Cになる)後にもう一度出力。その後にa[2]を出力しています。
これでBrainfuckは使えるようになりましたね!()
#オウム返しをするコード
Brainfuckでのコードは他の言語よりもかなり短くなる例もあります。それが、オウム返しをするプログラムです。
C++では
while(true){
std::cin >> str;
std::cout << str << std::endl;
}
と書きますが、Brainfuckでは入力に,
出力に.
を用いるので以下のように書けます。
,[.,]
うほっ、とてもシンプル。最初に入力しないとループの中に入っていかないことに注意してください。
#まとめ
いかがだったでしょうか。世の中にはチームで開発することを考えた可読性を重視したプログラミング言語や、オブジェクト指向プログラミング言語だけでなく、Brainfuckのような何のために作られたのかわからない、いかにも娯楽で使うようなプログラミング言語も存在します。日々の開発の合間に、こういったエンジニアの遊戯に触れてみてはいかがでしょうか。
NIT2.0では「自分の技術を多くの人に見てもらいたい」、「今は何もできないけど何か開発ができるようになりたい」、「チーム開発で大きなプロジェクトを立ち上げたい」、「プログラミング頑張りたい」、「学校を技術を中心にして盛り上げたい」というような気持ちがある人を歓迎しています。この記事を見てくれたあなたの参加を待っています!