プログラミング作法に関する世界観1(基本)です。
プログラミングの基本とは、演算、メモリ、制御構造、入出力です。
要素 | 説明 |
---|---|
演算 | 足し算、引き算、掛け算、割り算、余りの計算、べき乗、論理・比較、ビット演算など |
メモリ | 変数・型、配列、リスト、ハッシュ、集合、構造体、スタック、キュー、ツリー、グラフなど |
制御構造 | goto文、if文、switch-case文、for文、while文、関数・サブルーチン、クラスなど |
入出力 | コマンドライン入出力(標準入出力)、ファイル入出力、GUI、ネットワーク、データベースなど |
実際は、これらに加えて、さまざまな言語仕様とAPIを覚えます。たとえば文字列処理や正規表現などのAPIを使います。
これらの要素をマスターできれば、プログラミングはマスターできます。
後日注記:これらの四要素は、そもそもCPUの機能です。コンパイラは、これらの四要素をCPUにも分かる形に翻訳します。この翻訳されたプログラムが、実際のパソコンで動きます。
プログラミングのコツは、比較することです。
そのような場合がどういう状況なのか、想定し、正常な場合とどこが違うのかを比較することで、プログラミングができるようになります。
比較するということは、判断するということです。どのような場合にその処理をすべきなのか・してはならないのかを判断することで、ルーチンを書くことができるようになります。
そのため、プログラマになりたいのであれば、日頃から、普通のことをよく比較し、どのような場合とどのような場合が違うのかをよく考え、さまざまなことに対する「判断力」を養うようにするといいでしょう。
プログラミングにおいて重要なのは、「既に存在するルーチンを書き換える」ということです。
たとえば、関数はいったん定義したら基本的に書き換えません。引数を渡すことができるようにするだけで、関数そのものを書き換えるということは普通しません。
ですが、関数型プログラミングやオブジェクト指向プログラミングでは、関数あるいはデータ構造を書き換えて使うことがあります。
関数型プログラミングにおいては、関数は変数と同じものとして扱われます。なので、関数を関数の引数に渡したり、関数から関数を返すことができます。
オブジェクト指向では、継承の考え方を使うことで、データ構造と振る舞いを後から書き換えて使うことができます。
この意味することは、「汎用性」と「再利用」です。ひとつの関数を、単に使うだけではなく、後々別の処理をしたくなった時に書き換えられるように、柔軟性と汎用性のある形で実装し、それを再利用するということです。
プログラミングの基本とは、そのように「既に決められたものを後々変えられるように柔軟性のある方法で実装する」ということだと思います。まさに、それがプログラミングの「コツ」であると言えるでしょう。
プログラミングで最も分かっておくべきなのは、変数です。
変数は、数学でいう変数とは違って、データに対する「箱」のようなものであり、名前を付けて、型を設定します。
関数などへ引数を渡したり、関数から返り値を戻したりする時にも、型を設定します。
プログラミング言語によって、動的型付けと静的型付けがあります。
また、配列を使ってデータを複数同じ名前で管理出来ます。
配列を使う時には、インデックスを[0]のように表します。
また、繰り返しを使う時にも、iと言うインデックスを保持する変数を使います。
メモリは、スタックで管理するか、malloc()やfree()などを使って動的にヒープで管理します。
プログラミング言語によって違いは曖昧ですが、「文」と「式」を使います。
要は、繰り返しと条件分岐です。その上で、条件判断に式を使います。
プログラミングを難しいと言いますが、本来のプログラミングは全て、変数と繰り返しと条件分岐から成り立ちます。
ですから、やっていると、頑張って変数定義とif, case, for, whileを使っていれば、それで何でも作れます。
変数に何が入っているか、関数にどのように変数を送るのかを考えれば何とかなりますが、本当はもっと構造的に、知性から作れるようになると良いでしょう。
フローチャートを描く練習をしても良いでしょう。
プログラミングにおいて一番「自由」なのは、関数です。
どこからどこまでをどのように関数に分けていくのかは、完全に自由に任されています。
出来るだけ分かりやすい関数の設計を行いましょう。
また、実行ファイルだけではなく、共有ライブラリを使うこともあります。
2017-09-07より。
パソコンの関数は、何度も実行出来る。引数によって万能になり、返り値によって式になる。
関数は引数による自動の実行処理であり、引数が変わると処理する内容が変わる。
引数はその場その場で変えられるが、オブジェクト指向ならデータをオブジェクトにすることも出来る。
そして、オブジェクト指向言語になってくると、変数は全く違う世界である「オブジェクト」になります。
一つのクラスから、それぞれのオブジェクトを作成し、関数はメソッドと呼ばれるクラスと関連付けられたものを使います。
そして、継承やポリモーフィズムにより、オブジェクトは全て基本クラスから継承されたサブクラスに関連づけられます。
GUIアプリケーションの設計は、メインループかイベント駆動です。
メインループでは、プログラムに対する「メッセージ」を受け、無限ループの中で分岐することによって、機能を実現していきます。
イベント駆動では、ツールキットに対するイベント関数を設定していきます。
出来るだけ、グローバル変数を使うのではなく、オブジェクト指向で設計するようにしましょう。
まず、どこでも書かれているような内容に目を通すこと。
次に、システムの仕組みを知るために、さまざまなディストリビューションを使ってみること。
僕の場合、Gentoo Linuxと言うシステムの内部を分かりやすく知ることの出来るディストリビューションを見て、分かりました。
ネットの情報源から、さまざまな知を得ていくこと。それも、「確かに分かる方法」と「何を調べればそれが分かるのか」を良く調べること。
そして、最終的に、自分の手で研究した内容に関する文章を書くこと。それらの総合として、Linuxが分かります。
プログラミング言語の研究では、さまざまな人が言っている会話を参考にしましょう。
RubyからLispまで、IT業界にはさまざまなプログラミング言語が存在します。
それぞれの何が違っていて、何が出来るのか、本を読んでも分かりますが、ネットを見て積極的に会話していくことで、本来の言語の考え方が分かってきます。
ネットは馬鹿にせず、効果的に使いましょう。
基本的には、「知性」で考えることです。その知性を、自分独自に上手く作りましょう。
プログラムは厳密に動きます。絶対に、書いた通り以外の動き方をすることはありません。
ですから、色んな仕事をプログラムに直すために、日本語でプログラムのフローチャートを描く練習をすると良いでしょう。
まず、計算(四則演算)を行う記法があります。
四則演算は、()を使うことで優先順位を変えることが出来ます。
変数は、変数名と型を使って宣言します。宣言した変数しか、使うことは出来ません。
基本的に、変数は一時的な保管場所として使います。たとえば、A+B*Cを行うために、いきなりA+B*CとC言語で書くこともできますが、
x = b * c; a = a + x;
のように書きます。これが、もっとも最初のプログラムだと言えます。
実際には、変数はさまざまな用途で多用します(グローバル変数やポインタなど)。ですが、最初のうちは、「計算結果を一時的に保管するもの」だと思うようにしましょう。
プログラムは、上から順番に実行されます。
最初は、C言語ではmain()関数から実行されます。
関数や制御構文を使うことで、これを変えることが出来ます。
条件分岐によって、プログラムは条件式を判断し、それぞれ個別のブロックを実行したり、関数を実行したりすることが出来ます。
この時、条件式、論理式を使うことが出来ます。
等しい、等しくない、大きい、小さい、どちらも正しい場合、どちらかが正しい場合、どちらも間違っている場合などを判断できます。
ある処理を複数回に渡って繰り返し実行したい場合は、繰り返し式が使えます。
そして、繰り返しをネストすることで、繰り返しの中で別の繰り返しを行うことも出来ます。
通常は、変数iを使って+1ずつ加算することで、その繰り返しが何回目の繰り返しなのかを知ることが出来ます。
プログラミングの最も基本的な分野は、計算すること、変数に結果を格納すること、そして分岐・繰り返しを行うことの3つです。
分岐と繰り返しを上手く使いながら、計算し、変数に結果を格納すること。それだけで、多くのプログラムは成り立っています。
このコツを掴めば、簡単にプログラムを記述できるようになるでしょう。分岐と繰り返しが基本です。
条件分岐は、変数の値によって別のコードを実行するだけなので、単純で簡単です。ですが、繰り返しはどのように使うのでしょうか?
繰り返しは多くの処理に使いますが、基本的には、
1.足し算を3回して掛け算にするように、数学的な「回数」をコードで実行・表現するために使う。
2.入出力やファイル処理、あるいは変数や機械的な処理に使う。たとえば、「3回printf()を実行する」のように使う。
繰り返しは単に機械的に何度もするというだけではなく、アルゴリズムを表現する際に、「指定した回数実行する」という時にも使います。これは、アルゴリズムでは定番の方法で、変数を整理し、結果となる変数を返す、などといった「集中的処理」のために使われることが多いです。
変数は、データを入れる名前の付いた箱です。
計算した内容やデータを一時的に保管することや、さまざまな場所から受け取ったデータを抽象的なプログラムで処理することが出来ます。
配列は、一つの名前で複数のデータをインデックスとともに参照することが出来ます。
たとえば、data[0]からdata[5]までの6つのデータを保持することなどが出来ます。
そして、二次元配列のように、配列を二次元的に拡張し、配列の配列のように使うことも出来ます。
構造体は、関連するデータを、それぞれの名前とともに構造的に宣言し、構造的なデータとして保持することが出来ます。
配列はインデックスを使うことでそれぞれのデータは連続していますが、構造体はそれぞれの名前とともに分かりやすくデータを操作出来ます。
ポインタは、変数を名前で管理するのではなく、メモリアドレスで管理する手法です。
関数の引数としてポインタを使うことで、引数としてコピーでデータを渡すだけではなく、関数の中から外にあるデータを操作し、書き換えることが出来ます。
データを上手く保管しましょう。
ただ単純に変数に格納するだけではなく、アルゴリズムによって「整理された結果」を作り出したり、あるデータと別のデータを結びつけるデータ構造を実装したりします。
また、データは必ずしも計算結果ではなく、一つずつ増えていくカウントや、一意に決められるIDナンバーのように使うこともあります。ポインタでデータを参照し、「どこからでもアクセスできるようなデータの保管場所」として、FILE型のポインタなどを使うこともあります。
gotoで指定の位置までジャンプ。C言語などではあまり使われません。関数を使うことを推奨する人が多いです。
BASICのような言語ではGOTOはとても多用されていました。現在では、コードを読みにくく理解しづらくするという理由から、サブルーチンは関数にすることが推奨されています。
関数は、プログラムのそれぞれの部品を部品化し、抽象的な方法で扱う方法です。
main()関数の繰り返し構文からaction()関数を実行して、action()関数を繰り返し実行する、などと言った方法で、部品化してプログラムを構造化出来ます。
関数には、呼び出し時にオプションの変数を指定して実行するなど、引数を指定することが出来ます。
これにより、呼び出す場合によって異なる処理を実行するために、変数を使ってその処理を関数の中で書くことが出来ます。
返り値は、関数が実行された後で、その関数の実行結果を呼び出し側に返すことが出来ます。
ポインタを使って引数から変数を変えることも出来ますが、返り値を使うことで、関数に実行を依頼し、その実行の結果を返り値として得ることが出来ます。
関数はストリームです。つまり、「引数という変数が、関数の中を通り抜ける」ということです。
引数として与えられたデータは、関数の中を通り抜け、関数はそのストリームを中心に、さまざまな形式ばった「手続き」、つまり「処理」を行います。
プログラミングの基本は、このデータをどのように「通り抜けられるようにするか」というところに尽きると思います。
関数とは要するに、変数を関数に渡して、返ってきた値を得て、その上で、関数がさまざまな処理を実行する、ということです。
変数を関数に渡して、返ってきた値を得る、ということが、第一の目的です。
ですが、関数には2つのタイプがあります。それは、
1.関数に値を渡して、それが返ってくることが目的である関数。計算するためのファンクションがこれに当たる。
2.関数の処理自体を目的とする関数。返り値が無かったり、変数をそもそも渡さない関数、あるいは目的に応じて変数を渡し、その関数をOSやライブラリの機能を使って、処理を目的とする関数。printf()のような出力関数や、ファイル処理の関数などがこれに当たる。ファンクション(関数)ではなく、サブルーチンと呼ぶ場合もある。
関数は計算に使う場合と、OSの機能を使うために使う場合があります。
ライブラリ関数とシステムコールとして、OSにはさまざまな利用出来る関数があります。
たとえば、printf()のような入出力関数や、malloc()のようなメモリを得る関数などがC言語にあります。
ファイルを読み込む処理は、UNIXではopen(), close(), read(), write()で行います。
GUIではメッセージループあるいはイベント駆動でプログラムを作ります。
また、オブジェクト指向では、プログラムをクラス(オブジェクトの設計図)とオブジェクト(クラスに基いたデータ構造を作り、メソッドを抽象化する、さらに進んだデータの利用と関数の部品化の方法)から行います。
オブジェクト指向のプログラミングでは、関連するデータとメソッドをまとめて、クラスという階層的な設計図に記述します。
また、データを使う時は、データを「いっぺんに」確保して、クラスを元にオブジェクト(メソッドによって操作され、実際のデータが含まれる「もの=オブジェクト」のこと)を作成します。この時、この作成したオブジェクトのことを「インスタンス」と呼びます。
オブジェクト指向が何をやりたいかというと、それは2つあります。
1.データをいっぺんに作成したり破棄したりすること。
2.そのデータを関連付けられたメソッドによって操作すること。
データをいっぺんに作ってメモリ上にオブジェクトのインスタンスを作成し、そのオブジェクトを関連付けられたメソッドで間接的に操作する、それがオブジェクト指向です。
オブジェクト指向言語では、なんでもかんでも全てオブジェクトです。文字列も、配列も、数値も、型も、関数も、全てオブジェクトです。どれも同じようにインスタンスを作成した上で、メソッドによって操作します。
また、オブジェクト指向言語でも、ファンクションの考え方が使えます。オブジェクトを返す関数(メソッド)があるのです。そのため、JavaやRubyやDelphiなどでは、「.」をたくさん使ってメソッドを実行して返ったオブジェクトにさらにメソッドを実行する、などといったやり方も出来ます。
オブジェクト指向プログラミングでは、設計図として記述したクラスの内容に基づいて、同じ機能を持ったオブジェクトを「量産」することができます。
また、クラスを継承することで、既存のクラスを派生クラスとして複製し、データ構造やメソッドを追加・上書き(オーバーライド)できます。まるでdiffとpatchのように、既存のプログラムに変更を加えることなく、追加すべき機能だけを差分として追加して新しいクラスを作ることができるため、「差分プログラミング」と呼ばれます。
このような手法は、似たようなメソッドと機能を持ち、同じインターフェースからさまざまなオブジェクトを操作するGUIツールキットの開発やGUIプログラミングに適しています。(たとえば、サイズや位置を変更するメソッドはどのウィジェットにも必要。)
また、オブジェクト指向の先として、Visual C++/MFCによる、ドキュメント・ビュー・アーキテクチャがあります。DocumentクラスとViewクラスを継承することで、プログラムの主役であるビューの表示とドキュメントの読み書きを統一的なフレームワークから行うことができます。
そして、ATL/COMのようなコンポーネント技術を使うことで、アプリケーションの中で独自のコンポーネントを開発し、既存のコンポーネントを使用したアプリケーションを使うことができます。大きなアプリケーションの開発では、このように、さまざまなプログラムの部分をクラスやコンポーネントとして設計します。DelphiやVisual C#のようなオブジェクト指向の開発環境では、フォームデザイナーやコードエディタを使用することで、ヴィジュアルで分かりやすくコンポーネントの使い方や作り方を体得することができます。GUI初心者にはDelphiがおすすめです。
基本はそれだけです。プログラムは自由に作ってパソコンで実行出来るので、頑張って面白いプログラムを作ってください。
プログラミングには、さまざまな分野と応用技術があります。以下は、コードをどのような目的に書くか、という分野です。
分野 | 説明 |
---|---|
システムプログラミング | OSやカーネルのように、プログラムを実行させるためのプログラムの領域。 カーネルのマルチタスクや仮想CPU・仮想メモリ、 それからファイルシステムや低レベルなデバイスドライバ・ネットワークなどを構築する。 主に、アセンブリ言語とC言語のような低レベル言語を用いる。 |
入出力 | 入力と出力。 コマンドラインへの文字列の表示や、コマンドラインによる文字入力を行う。 後述するファイル処理とも大きく関わる。 C言語ではprintf()やscanf()のような関数を使う。 |
ファイル処理 | ファイルの読み書きや、削除、名前変更、存在確認、あるいはディレクトリの操作などを行なう。 ファイルはテキストファイルの場合もあれば、画像ファイルやバイナリファイルである場合もある。 ファイルはローカルにある場合もあれば、ネットワークにある場合もある。 C言語では主にopen(), close(), read(), write()関数、バッファを用いたf*関数を使う。 |
文字列処理 | 文字列の処理、特に編集や検索・置換を行う。 |
パターンマッチング | 正規表現を用いたパターンマッチングを行う。 |
メモリ管理 | メモリを静的あるいは動的に割り当てる。 スタック領域に作成した変数はひとつひとつ破棄される。 malloc()でヒープ領域に作成した変数はfree()で自ら破棄しなければならない。 |
プロセス管理 | プロセスを作成・破棄する。 シグナルやプロセス間通信のような仕組みも使われることがある。 |
圧縮・解凍 | アーカイブデータの圧縮・解凍を行う。 |
分野 | 説明 |
---|---|
配列とリスト | 配列とリストを静的・動的に作成する。 |
ポインタと構造体 | ポインタは変数のメモリアドレスを指す変数。 構造体は多くの変数をひとまとめにした変数群の変数。 C言語などでは構造体のポインタを使うことで疑似オブジェクト指向ができる。 システムプログラミングにおいてもポインタは重要。 |
クラスとオブジェクト | クラスとして、共有されるデータとインターフェースとして用いるメソッドをひとまとめにし、 使う側はアプリケーションのようにオブジェクトのインスタンスを作成する。 |
継承・インターフェース・例外 | オブジェクト指向プログラミングでは、継承・インターフェース・例外を多用する。 継承とインターフェースを使うことで、一部分だけを書き換えたプログラムや、 共通のインターフェースを持ったプログラムを作ることができる。 特に、GUIプログラミングの場合は、テキストボックスを継承して自分のテキストエディタを開発できる。 また、例外を使うことで、C言語ではif文を用いて書いていたエラー処理をエレガントに記述できる。 |
スレッドと並列処理 | 複数のスレッドをマルチスレッドとして作る非同期処理。 最近ではgolangのような専用の並列処理言語を使うことも多い。 |
分野 | 説明 |
---|---|
ネットワーク | ネットワークを介してプログラムを実行する。 ソケットのAPIでは互いに関係を持った2つのソケットが生まれた時、 片方に書き込んだデータをもう片方から読むことができる。 |
CGI/サーバーサイド | HTMLを表示するブラウザに対し、HTTPサーバーがHTMLを表示する時、 サーバーサイドでHTMLを処理する技術。 掲示板やチャットからSNSまで、 ブラウザで表示できるページをプログラミングして作り出すことができる。 Perl/CGIを使うことが多かった分野だが、 今ではJava, PHP, Python, Rubyのような言語も多く使われている。 |
Webフレームワーク | Webフレームワークと言う技術では、 MVCという設計方針でとても簡単に大規模なWebサービスを作ることができる。 |
JavaScript/クライアントサイド | クライアントサイドのプログラムでは、 HTMLをブラウザが表示する際にブラウザ側でHTMLに対してスクリプト処理を追加する。 サーバーサイドではHTMLの「構成そのもの」を作り出すのに対して、 クライアントサイドでは「ダイナミックな動的操作」を実現する。 たとえばページの最下部までスクロールしたらその時点で サーバーからHTMLを動的に読み込む処理などに使われる。 |
セキュリティ・暗号 | セキュリティや暗号で、安全なネットワークを構築する。 |
分野 | 説明 |
---|---|
グラフィックス | グラフィックス処理は、2Dのグラフィックス処理で3Dのグラフィックス処理がある。 Windowsでは、GDIというAPIでペンやブラシを使って 直線や四角形、円などを描くことができる。 |
3Dグラフィックス | 3Dグラフィックスでは、 OpenGLやDirectXのような技術を用いて3Dのグラフィックス処理を行う。 たとえばゲームのような美しいCG画像を作ることができる。 |
GUIツールキット・GUIコントロール | ツールキットとコントロールでは、 コマンドラインと異なりGUIのフォームを表示し、 ボタンやメニューのクリックなどのイベントや、 テキストボックスに入力されたGUIのデータ情報を処理してプログラムの中で使用し、 さまざまなフォームを表示する。 |
ドキュメント・ビュー | MicrosoftによるC++のGUIプログラミング・フレームワーク。 ドキュメントクラスとビュークラスにプログラムを分離し、 その相互作用でGUIプログラムを作成する。 |
動画や音声の再生・ストリーミング | 動画や音声の再生とストリーミング。 |
分野 | 説明 |
---|---|
科学計算 | 科学的な計算。平方根や三角関数のような基本的なものから、 後述する人工知能・AIまで。 |
人工知能・AI・機械学習・ニューラルネットワーク | 人工知能・AI・機械学習・ニューラルネットワークは、 ディープラーニングのような機械による学習を積み重ねて人間を超える頭脳を発揮する。 昔はLispで作られることが多かったが、今ではPythonが多く使われている。 |
言語処理系 | 言語処理系とは、コンパイラやインタプリタのこと。 自分のプログラミング言語を作る。 |
分野 | 説明 |
---|---|
組み込み・モバイル | 組み込みやモバイルの分野では、クラスライブラリの水準を下げて、 必要最低限のAPIを用いてプログラミングしたり、 タッチパネルなど独自のプログラミングを行う。 |
ハードウェア | ハードウェアの分野では、アセンブリ言語・マシン語を用いてプログラミングを行う。 |
ネットワーク機器 | ルーターやハブなどのネットワーク機器のプログラミングを行う。 |
ゲーム | ゲームも一分野の一つである。 |
大型コンピュータ・高信頼システム | データセンターなどでは、たとえばクラスタ型のデータベースでは、 データベースの冗長化(二重にデータをコピーしておく)やコンテナ型の仮想化など、 さまざまな高信頼システムの構築を行う。 |
Web上で各種言語をコンパイル・実行できるpaiza.IOを使うことで、Web上で簡単に各種言語のソースコードを記述・コンパイル・実行できます。C/C++, Java, Perl, PHP, Python, Rubyなどさまざまな言語に対応しています。初心者はこれで開発を練習し、各種言語のコードに触れても良いでしょう。
最近買った本の中で良かった本として、「おうちで学べるプログラミングのきほん」があります。プログラムの動く仕組み、OSの仕組み、プログラミング言語、JavaScriptとC言語によるプログラミング、オブジェクト指向のモデリングなど、「コンピュータに関係するさまざまなことを総じて知ることができる書籍」です。
なぜか、僕のこのホームページに書いた内容と良く似ています。きちんとしたプログラマの方が書いた本なので、一度手に取って読まれてはどうでしょうか。
条件分岐はプログラムの基本。どのような「条件の場合」にどのような「処理」を行うかを書く。
ループは単に同じ処理を繰り返すだけではなく、イベントループのように「常にイベントを待ち続ける」とか、「それぞれの場合に処理を適用する」(たとえば正規表現でのパターンマッチングを全ての行に適用する)などのように使うこともある。
関数は設計の基本。汎用的な関数を作り、「共通のコアルーチンを上手く呼び出す」ようにプログラムを設計・実装する。
さまざまなコードの場所から「ライブラリ」のように関数を呼び出したり、「データを処理する時のラッパーインターフェース」として使ったりすることもある。
配列は連続的に要素が並んで格納されるが、リストは次のデータへポインタを紐づけするようにデータを連結する。
配列は検索やアクセスがしやすいが、リストは追加や削除がしやすい。
以下のページから、@ITの連載記事の一覧が見れます。
Wikibooksより。
書籍