第4回 - プログラムを作るとはどういうことか
復習
- 昔は回路を作りなおしていた。現在は命令の列。(= ソフトウェア)
- 記憶装置に記録されている。
- 演算装置が解釈して実行する。
- 以下のことが可能:
- 簡単な計算をする。
- データを記憶装置から読み込み、書きこみ、複製できる。
- 入力装置から受信したり、出力装置に送信したりできる。
- 文字、画像、音声などすべて 0 と 1 であらわす。
現在では、プログラム (=ソフトウェア) がコンピュータの用途を決める。
「手続き」の考え方
コンピュータ・プログラムは「手続き」とも言われる。
実行する順序がとても重要。
例: カレーの作り方
- 肉・野菜を炒める。
- 水を入れる。
- ルウを入れる。
- 火を止める。
例: 自動販売機
- お金を入れる。
- 商品を購入する。
- おつりを出す。
- 1. に戻る。
これらの順序が違ったらうまくいかない。
Windows 用のプログラム abc.exe
- バイナリエディタで 0 と 1 を見てみる。これはすべて手続き (命令の列) をあらわす。
- 「
abc
」の部分を「123
」に書き換えてみる。
- 「
abc
」の部分を「hello world
」に書き換えてみる。
ふつうは、こんな 16進数を直接書いたりすることはない。
「プログラミング言語」というものを使って書く。
内部の動き
- 原理的には … 入出力装置に 0 と 1 を送受信してハードウェアを制御する。
- 現実には … 現代のプログラマはほとんどハードウェアに直接0/1を送ったりしない。
- あまりにも沢山の種類があり、いちいち使い方を調べてらんない。
- 0 と 1 だけで制御するのはあまりに複雑で、やってらんない。
現在、こうした機能の多くはオペレーティングシステム (OS) と
呼ばれるプログラムが提供している。
たとえば、「画面に文字を表示する」という OS の機能を使えば、
文字の構造や、ディスプレイの種類などは考えなくてもよい。
プログラムを作るさいには、OSの機能の使い方も学ばなければならない。
しかし使っている OS が変わると作成したプログラムを変更しなければならないことが多い。
プログラムの作成のことを
「プログラミング」あるいは「ソフトウェア開発」という。
ソフトウェア開発の手順
- 作りたいものと実現方法を決める。
- 実際のプログラム (文書) を書く。
普通これには文書作成ソフト (例. メモ帳) を使う。
- 0と1の列に変換して実行する (あるいは、変換せずに実行する)。
変換のためには、特別なソフトウェアが必要である。
- 動かなかったら 2. に戻る。
インタプリタとコンパイラ、仮想マシン(VM)
コンピュータがプログラムを実行するには、
おもに2つの方式がある。プログラムの作成はそれによって変わってくる。
どちらも「命令を解釈して実行する」という部分は変わらない。
方式1. ハードウェアによる実行 (ネイティブ)
演算装置が直接 0 と 1 を解釈する。
- 非常に簡単なことしかできない。
- プログラムを作るのが大変。
- 通常はコンパイラを使ってプログラミング言語から 0 と 1 に変換する。
方式2. ソフトウェアによる実行 (インタプリタ、仮想マシンあるいはエミュレータ)
通常インタプリタ、仮想マシン (VM) あるいはエミュレータなどと呼ばれる
ソフトウェアがプログラミング言語を解釈する。
このソフトウェア (プログラム) そのものは
ハードウェアの演算装置によって解釈される。
- プログラムを作りやすい。
- 効率は悪い (二重に解釈されるため)
プログラミング言語
実行方式と用途によって、プログラミング言語を使いわける。
どの言語も、基本的には命令の列を記述するためのものである。
- ある言語を使うと、プログラムが短くてすむが、実行が遅い。
- ある言語を使うと、プログラムが速く実行できるが、複雑でわかりにくい。
- ある言語を使うと、OS の機能をより簡単に使える。
- ...
- 完璧なプログラミング言語というものは存在しない。
ネイティブで実行される言語
- アセンブリ言語
- C/C++
#include <stdio.h>
int main(int argc, char* argv[])
{
char s[100];
int i;
for (i = 0; i < 10; i++) {
s[i] = '*';
s[i+1] = 0;
printf("%s\n", s);
}
}
インタプリタ/VMが解釈する言語
- Java (JVM)
public class Program {
public static void main(String[] args) {
String s = "";
for (int i = 0; i < 10; i++) {
s += "*";
System.out.println(s);
}
}
}
- C# (CLR)
- Javascript (ブラウザ)
ほかにも何百という言語がある。
最初のプログラム (Javascript)
ブラウザで F12 キーを押し、以下の行を入力する。
var s = ""; for (var i = 0; i < 10; i++) { s += "*"; console.log(s); }
プログラミングと数学
「プログラムを作るためには数学が得意でなければならない」 - ある程度は真実。
ただし、計算が得意である必要はない。
例1:
n 個の * をつなげて三角形を表示する。
この三角形を右寄せにするためには、何個の空白をつければよいか?
例2:
幅w1ピクセル × 高さh1ピクセルの
縦長の画像がある。これを、
幅w2ピクセル × 高さh2ピクセルの
横長のウィンドウにフィットするように表示したい。
横幅は何ピクセルに縮小すればよいか?
基本的に、コンピュータは「あたりまえ」のこと、
つまり普通の常識で理解できることしかしない。
ただし、あまりにも大量の処理を高速にやるので、
常識からはかけ離れたことをしているように見える。
常識から離れたようにみえる処理
- 数百万桁におよぶ複雑な計算 -
0 と 1 を一桁ずつ処理することによって答えを得ている。
- 将棋、チェスのような「賢いふるまい (人工知能)」 -
すべての組み合せを片っぱしから試すことによって答えを得ている。
- 検索エンジン -
すべての単語であらかじめ検索した結果を記憶しておくことによって
一瞬で答えを出している。
- チャット -
複数の層に分けて実装することで携帯電話とPCからのデータを統一して扱える。
サーバでパケットを中継することで双方向の通信が可能。
あらゆる手品には仕掛けが存在する。仕掛けがわかってしまえば
「なあーんだ」となる。ある意味で、プログラミングは手品に似ている。
機械は万能ではない
- 情報は単なる 0 と 1 の羅列にすぎず、善悪は区別できない。
- すべてのプログラムが役に立つことをするとは限らない。
→ 悪事を働くプログラムも沢山ある。
- すべてのプログラムが正しいとも限らない。(不具合、バグ)
→ 「プログラムはあなたの意図したとおりには動かないが、
あなたの書いたとおりに動く」
→ GIGO (Garbage In, Garbage Out)
複雑すぎて書いた本人もよくわかっていないことがありうる。
- 技術的負債 (Technical Debt) という考え …
「あまりよくできていないプログラム」は借金のように後からツケがくる。
やりたいことを決定するのが大変
- 何をしたいのかがわからない。
- 何千もの決定をする必要がある。
- やりたいことが説明できない。
やりたいことを他人に伝えるのが大変
- プログラムは長い時間をかけて作られる。
- 伝えることは山のようにある。
- いろいろな方法がある: 日本語、プログラムのコード、図。
- 「他人」は1ヶ月後の自分かもしれない。
正しい動きを保証するのが大変
- 何億もの考えられる組み合わせがある。
- すべての事態を想定できない。
- 悪人はつねに想像もできないような手口を使ってくる。
機能を修正する・追加するのが大変
- すでに動いているソフトウェアを止められない。
- 機能を変更すると、想像しなかった悪影響が出るかもしれない。
- 古いプログラムと新しいプログラムとつじつまを合わせるのが大変。
- 使っているOSの機能が変わると、
プログラムもそれに合わせて変更しなければならないことが多い。
ソフトウェア開発を支援する技術
- 決定するための技術 - アジャイル開発
- 記録するための技術 - バージョン管理、UML
- 保証するための技術 - 自動テスト、型検査
- 修正・追加のための技術 - バグ管理システム
どれも根本的な解決ではない。
このように、プログラマは「他人にわかりやすく伝える」ことが重要な職業であるので、
実際には数学的なものの考え方や技術的知識だけでは不十分である。
とくに大きなプロジェクトになればなるほど、国語力や
他人に連絡・報告したり議論したりする社会的スキルが重要になる。
Copyright (c) 2015 Yusuke Shinyama