第十二回-02 コンソールアプリケーションってどう使われているの?

第十二回-01で「手続き型とオブジェクト指向」および「コンソールとGUI」について触れ、
現在この講義で学んでいることが、プログラミングという分野でどのような位置付けにあるかを俯瞰した。

現在やっていることの意味を理解することはモチベーションを維持する上で非常に重要だと思うが、
前ページの解説により、逆に「GUIをもったまともなプログラム作るまでにはあまりにも学ぶことが多すぎる」
と恐れをなしてしまう人が多かったとしたら、本末転倒であろう。

そこで、本ページは「ここまでの範囲でもかなりのことが出来る」ことを強調するページとしたい。


プログラミング習得への道

第十二回-01の内容を理解すると、プログラミングを習得するまでの道筋の一つとして というルートがあることがわかると思う。
最近は、もう最初からオブジェクト指向を学ぶ以下のルートを推奨する人もいる。 このルートを取ったとしても、if 文による条件分岐や for 文などにより繰り返しおよび配列などは学ぶ必要はあるので、
いずれにせよプログラミングの習得までは遠い道のりがあるように思える。

ところで、理系の大学では、3年生や4年生でプログラミングについて学び、その知識をもとに卒業論文を執筆し、
さらには学会発表までこなして卒業してゆく学生も多い。
教育者としては上記のルートを通ってプログラミングを習得することを推奨したいのだが、
現実問題、限られた時間しかない学生達が、みな上のようなルートを辿っているかというと必ずしもそうではないように思える。

学生達が卒業のための研究でプログラミングを習得する際、 経験上、以下のような方法を取っていることが多いと思われる。
  1. ある程度機能が実装され動作する状態にある GUI プログラムを渡され、それを改良することで研究を開始するケース

  2. コンソールアプリケーションまでの知識で数値計算などを行い、研究結果を出すケース
1. の「あらかじめ渡されたGUIアプリを改良するケース」は 第三回-02 配布したGUIアプリケーションの概略で既に皆さん経験済みである。
既に動くプログラムをもとに改良を加えたり、新たな処理を実装したりすることを目指すわけだが、
この場合、オブジェクト指向プログラミングや関数、ポインタの知識が不十分でも、それらしくプログラミングができるレベルには到達できる。
私の研究室を卒業した大学院生も、オブジェクト指向プログラミングを完全に理解していたわけではないと思うが、
この方法で Visual C++ による GUI プログラミングを習得して卒業していった。

教育者として、そのようなショートカットによってプログラミングを習得することを必ずしも推奨はできないが、
現在のように必要とされる知識が膨大となっている状況ではやむを得ないとも思う。
(理想的には、そのようなショートカットでGUIプログラミングに対する自信をつけて、
その後で遡ってオブジェクト指向プログラミングを学び直すのが良いのかも知れない)

一方、2. の「コンソールアプリケーションまでの知識で研究するケース」も多く行われている。
つまり、皆さんのここまでの知識、すなわち のみで研究を行うケースである。教育者としてはせめてこれら全てを習得して欲しいと思うが、
これらのうち「条件分岐」、「繰り返し」、「配列」だけで卒業研究を行っている学生は日本中で恐らく多いと思う。
(決して推奨できないが、「関数」は全ての処理を main 関数に書くことで、「ポインタ」は main 関数のみで頑張ることで回避できてしまう。)

以下では、コンソールアプリケーションで何ができるのかについて少し紹介したい。


本ページで紹介する例

さて、皆さんはコンソールアプリケーションを 等と思っていないだろうか?

実際には、先程述べたように「コンソールアプリケーションだけで卒業研究や学会用の研究は行える」のである。
更に言えば、「GUIアプリケーションよりコンソールアプリケーションの方が望ましい場合もある」。

そのような例の一つとして、 「強化学習による Cart-Pole Balancing」という制御の問題を考えよう。
この問題は制御工学の例題として良く取り扱われるものであるが、
下図のように車の上に逆さの振り子を取り付け、この振り子が倒れないように車に与える力 F
(または車にとりつけたモーターへのトルク) を制御しよう、という課題である。

制御工学の分野では、振り子が真上を向いた状態の周辺のみを取り扱い、いかに振り子を倒さないかが問題とされるが
このアプリケーションでは「振り子が真下にあるときに振り子を振り上げた後にそれを倒さないようにすること」が課題となっている。



そして、このアプリケーションは「学習理論」を用いて、
最初は制御がうまくいかないが、試行錯誤を何度も繰り返すことにより最終的な制御方式を学習して獲得する
ところにポイントがある。

一回の試行に時間を 20 秒 (実時間ではなくシミュレーション内の時間) を与え、
もし、20 秒に達するか、その前に車が壁に当たったり、
振り子が複数回転してしまうとそこで強制的に次の試行に移るようにプログラミングしている。



最初のうちは振り子を上に向けることすら出来ない状態だが、何回も試行を繰り返すことにより、制御方式が学習により獲得される。
アプリケーションの「After Learning」ボタンを押せば、学習後の状態を見ることができる

このように完成後のアプリケーションのみを見せると、このプログラムは最初からGUIアプリケーションとして作られているように見えるかもしれないが、
実際にはコンソールアプリケーションとして作り始められている。

以前の職場で、この問題を卒論生の研究課題として課したことがあった。
彼は皆さんとほぼ同じでプログラミングは3年生のときに講義で少しかじった程度であった。
しかし、彼は苦心の末「if 文」、「for 文/while文」、「配列」だけの知識を駆使して この研究を行い無事卒業していった。
(「関数」も「ポインタ」も使わなかった)

皆さんも時間をかければこの程度のプログラミングはできる知識は既に持っている。少しヒントを出してイメージを掴んでみよう。
例えば、台車と振り子の状態を格納するための変数がまず必要である。

double x, v;          // 位置 x と速度 v
double theta, omega;  // 角度 θ と角速度 ω

また、0<t<20 の時間だけシミュレーションを進めるには 時刻 t と時間刻み dt を用いて以下のように書けるだろう。

double t = 0;       // 時刻を表す変数

while(t < 20){  // t=20 になるまで繰り返し

  // 運動方程式を数値的に解いて台車と振り子の状態 (x, v, θ, ω) を一ステップ進める処理 (省略)

  // もし車が壁にぶつかったら、ループを抜け出す処理 (省略)

  t = t + dt;   // 時刻を dt だけ進める
}

また、上記を「1試行」とした際に、それを10試行繰り返すのであれば、全体を for 文で繰り返すことになる。

for(int i=0 ; i<10 ; i++){
  double t = 0;       // 時刻を表す変数

  while(t < 20){  // t=20 になるまで繰り返し

    // 運動方程式を数値的に解いて台車と振り子の状態 (x, v, θ, ω) を一ステップ進める処理 (省略)

    // もし車が壁にぶつかったら、ループを抜け出す処理 (省略)

    t = t + dt;   // 時刻を dt だけ進める
  }
}

このような形でプログラムをどんどん膨らませて行って、目的のアプリケーションに近づけて行くわけである。
なお、「運動方程式を数値的に解いて」とあるが、これは実は1年生で Visual Basic を学んだ際に一度登場している。

この課題は以前の職場で卒論生にコンソールアプリケーションとして作成してもらったが、
その後、私が自分で作り直した際もやはりコンソールアプリケーションからスタートし、その次のステップとしてグラフィック表示を作成した。
コンソールアプリと GUI アプリとでそれぞれの役割は以下のようになっている。 こうしてみると、少なくともこのアプリケーションでは GUI アプリケーションは副次的な意味しか持たないことが分かる。

先回りして述べておけば、上記のように「コンソールアプリを GUI アプリとして拡張する」ことは、
オブジェクト指向の考え方に則り、下図のように処理をオブジェクトごとに切り分けておけばそれほど大変な作業ではない。



さて、それでは何故この例ではまずコンソールアプリケーションで研究を開始したのだろうか?
最初から GUI アプリを作ったのではいけないのだろうか?



コンソールアプリケーションの方が有利な場合

さて、この例の場合にコンソールアプリケーションから研究をスタートした理由は以下の2つである。
  1. GUIアプリケーションよりもコンソールアプリケーションの方が圧倒的に高速である

  2. 標準 C/C++ に基づくコンソールアプリケーションであれば、多くの環境でも実行できる。学校にある計算用サーバなども活用できる
まず、一つ目の計算速度に関して。
Java のコンソールアプリケーションで、10 回の試行にどれだけの時間がかかるかを調べるた様子が下図であり、
約 50 秒くらいかかっていることがわかる。



ただし、この時間はランダム性があるので毎回変わり得る。10回時間を計測し、その平均を取ったのが以下である。
比較のために C++ のコンソールアプリも作成し、それでかかった時間も掲載した。

方法 時間 備考
C++ コンソールアプリ   33.8 [秒]   Pentium M 1.2 GHz / Linux (Fedora Core 6) / g++ 4.1.2 / コンパイルオプション -O3 -ffast-math
Java コンソールアプリ   52.1 [秒]   Pentium M 1.2 GHz / Linux (Fedora Core 6) / Java 1.6 u 10
Java GUI アプリ (アプレット)   132.2 [秒]   Pentium M 1.2 GHz / Linux (Fedora Core 6) / Firefox 3.0.3 / Java 1.6 u 10

この表から読み取れることは、以下の通り。 そのようなわけで、処理速度が重要な場合、GUIアプリケーションよりはコンソールアプリケーションの方が有利である
というのがまず一つ目のポイント。

次に、コンソールアプリケーションを用いる二つ目の理由
標準 C/C++ に基づくコンソールアプリケーションであれば、多くの環境でも実行できる」 という点について述べよう。

第十二回-01の冒頭の図を理解すれば、
標準 C/C++ に基づくコンソールアプリケーションであれば、Linux や Mac OS X でもアプリケーションを実行できる
という点は理解できるであろう。
逆に言えば、C++ で Windows 用の GUI アプリケーションを作成してしまうと、それはもう Windows でしか実行できない。

実は、本学には Linux が動作している「計算用サーバー」が存在し、登録した教職員や学生は
自分で作成したプログラムをそのサーバーで走らせることができる

(理系の大学ではそのようなサーバーを持っている大学は多い)
そのため、Linux でも動作するようプログラムを書いておけば、その計算用サーバーの恩恵にあずかれる、というメリットがある。

そのイメージを描いたのが下図である。自分が家にいようが、キャンパス1にいようが、キャンパス2にいようが、
インターネットに接続できてログインできる環境にあれば、計算を実行したり計算結果を知ることができる、というわけである。
なお、長時間 (丸1日とか、1週間とか) 計算をし続けるプログラムを動かしたい場合は、
サーバーで計算を開始後、ログオフしてもサーバーで計算し続けられるので、終りそうな頃になってから再びログインして結果を確認すればよい。

「Linux なんて使ったことないし俺には関係にないよ」という人も、ちょっと便利そうな気はしないだろうか?



ちなみに、そのように計算用サーバーを使うのは決して特殊なことではない。
以下のように計算サーバーにログインして、どのようなプログラムが走っているか表示してみると、
電気系の教員と金丸に混じって、機械工学科の学生が fflowS というプログラムを動かしていることがわかる。
(名前からして流体力学の解析を行うプログラムであろうか)



このように、外部サーバにログインしてコンソールアプリケーションを走らせる、というのは普通に行われることである。
GUI アプリケーションでそのようなことは、不可能ではないが敷居が高いし、
また GUI アプリケーションをネットワーク越しに使うことはマシン的にもネットワーク的にも負荷が高いのであまりお奨めできない。



←第十二回-01 手続き型プログラミングとオブジェクト指向プログラミング/コンソールとGUI第十二回-03 オブジェクト指向プログラミングとは→

非情報系学生のための C/C++ 入門に戻る