プログラミング作法に関する世界観3(エキスパート)です。
プログラミングのコツは、設計、再利用、開発手法です。
まず、プログラミングの世界では、コードを書いている時間は、全体の時間のほんの数割にすぎません。
システムを構築する上では、コードを書いている時間よりも、そのシステムを「考えている時間」のほうが、多くを占めます。
プログラミングの手法についてまず言えるのは、「コードを書く前にきちんと設計しろ」ということです。
確かに、プロトタイプを作ってそれを改良していくような手法もあるにはありますが、それは例外的であり、実際のプログラミングでは、コードを実装するよりも前に全体の設計を考えます。
どのようなデータ構造を使うのか、クラスのそれぞれのやり取りや継承・包有の関係をどのように行うのかを考え、制御のモデルに何を採用し、ユーザーインターフェースはどのようになり、プログラムでは詳細にどんな機能を必要とするのか、プログラムはどのようにモジュール化されるのか、ということを、「作るよりも以前に考える」ということが必要です。
きちんと設計されていないプログラムは、作っても動きません。作って動くプログラムを開発するためには、「作る前に設計をする」ということが必要です。
次に、プログラミングの手法について言えるのは、再利用することです。
すべての会社で開発するプログラムが、そのプログラムのためにオリジナルで書かれているかというと、それはそうでもありません。
以前に作った機能と同じ機能を実装するためには、再利用性を考える必要があります。
コンポーネントを再利用するということは、単にコードを流用して埋め込むだけではありません。オブジェクト指向の考え方を用いた、再利用可能なコンポーネントを事前に開発しておき、どんな場合においてもそのコンポーネントを汎用的に利用できるようにします。
これは、自社で開発する場合だけではありません。たとえばRubyのgemsのように、ネット上に存在するさまざまなモジュールを使うということも考えられます。
このようにすることで、元のコンポーネントを流用し、ゼロ秒でシステムを開発できます。
最後に、プログラミングについて重要なのは、コードではなく、開発手法です。
これはすなわち、プロジェクトマネジメントです。プロジェクトをどのように成り立たせるか、ということです。
システム開発は、通常、上流工程でシステムエンジニアが大まかな設計をし、設計が固まった段階で、下流工程の「コードだけを書くプログラマ」へと流れていき、プログラマが具体的なコードを言われたとおりに実装していくことで成り立ちます。
システム開発は、コードを書く前に設計すべきであるとは先にも言いましたが、通常は設計をベテランのシステムエンジニアがし、詳細な実装は若いコーディング専門のプログラマにさせます。
プログラミングは、このように成り立ちます。
プログラミングについて言えることは、目的とするプログラムだけを書くのではなく、そのプログラムに必要なベースとなるプログラムをあらかじめ書いておくということです。
これは、どんなクラスにおいても必要となる、基盤となる仕組みやフレームワークを、あらかじめ書いておき、そのプログラムをどんなプログラムでも再利用するということです。
たとえば、やねうらお氏の以下の書籍には、C++で開発する上での、スマートポインタやマイクロスレッドなどを始めとする、主にゲームのための、しかしながらゲーム以外の開発にも使えるような、汎用的なテクニックが紹介されています。
そのように、プログラムとは、最初から最後まですべてそのプログラムのために書くのではなく、ある程度の「プログラムを書く仕組み」を作っておいた上で、その仕組みを使ってプログラムを書いていく、という方法をよく使います。
これは、C++のゲームプログラミングだけではなく、たとえばRuby on RailsのようなWebプログラミングについても言えることです。あらかじめWebに必要な機能をRailsというフレームワークに持たせておいた上で、Railsを使って具体的なWebサービスを作るのです。
また、Lispという言語には、「Lispを拡張することで、プログラミング言語そのものを自分で作っていく」という発想があります。これはLispハッカーのポール・グレアム氏の述べていることであり、LispプログラマはLispそのものを拡張して、Lispを自らの理想に近い言語に仕立て上げていき、その言語を用いてプログラミングを行うのです。
ANSI Common Lisp (スタンダードテキスト)で言われているように、計画が完璧であり、人間が全知全能であることを前提にプロジェクトを実行しようとしてはいけません。
まずプランがあり、次にインプリメントがあるというモデルでは、プランが完全に完璧な計画で行われることが前提であり、また、人間が間違いを絶対に犯さないことが前提となりますが、これは絶対にありえません。
人間が間違いをするものだ、ということを前提に考えなければ、ことさら人間が間違えやすいプログラミングの世界では、適切にプロジェクトマネジメントをすることはできません。
最初から計画にも流れにも間違いがあるものであるとし、その間違いが起きたとしてそれを修正するコストが一番低くなるように、プロジェクトのやり方を考えるべきなのです。
この書籍では、そのためにLispの持つ特性は有用であると述べられていますが、「人間は間違いを犯すものであり、計画が完璧であると考えるな」とすることは、どんなプログラミング言語にも、どんなプロジェクトであっても大切な原則であると思います。
計画だけの話ではなく、いったん作ったものが失敗作になった時、それを修正するのに多大なコストがかかるというのは地獄です。できるだけ、どんな間違いや失敗があってもそれを修正するコストが最低になるようにするべきです。
僕が思うに、プログラムの設計を行う上でのコツは、「いきなりコードで考えないこと」です。
いきなりコードで考えると、どうでもいい雑多な細かいことを考えなければいけません。
そうではなく、まず、もやもやした概念的なイメージを考え、そのイメージを実際にどのように実現すればいいか、ということを抽象的に考えるのです。
プログラムの全体の構造を、大まかに想像力で考えて、それをどのように設計すれば、プログラムが上手く書けそうか、ということをまず考えて、その上でコードを書くべきなのです。
なので、最初の時点では、コードではない形で、もやもやした概念的なイメージを考えましょう。そこから正しい設計が生まれるのです。そして、その設計通りにプログラムを記述すればいいのです。
また、設計をする上で効果的なのは、既にあるプログラムに付け足したり付け替えたりといったことを考えることです。なんらかの単独のプログラムがあって、そのプログラムを別の場所から使ったり、あるいはプログラムの内容に別のプログラムの内容を付け加えたり付け替えたりということを考えます。そのように考えることで、機械部品を作るようにプログラムが成り立ちます。かっこいい言葉で言えば、「コンポーネント指向の考え方をする」ということが大切なのです。
2023.01.09
プログラミングという作業は、そんなに難しくありません。
プログラミングの基本は、「APIを作ってそれを呼び出す」ということです。
たとえば、Webサービスを作るのであっても、GUIアプリケーションを作るのであっても、まず最初に取り掛かるべきことは「APIを作ること」です。
APIの中で、データベース処理をしたり、UIの表示処理をしたり、ファイル処理をしたり、共有されるデータを変更・参照したり、アルゴリズムを書いたりします。
そして、そのAPIを、適切な場所から適時に呼び出せば、それだけでプログラミングはできます。
そう、APIを作ってそれを呼び出すだけで、プログラミングは簡単にできるのです。
後日注記:APIを作る上で、どのメソッドからも共有される「共有データ」が必要になります。そのため、オブジェクト指向の考え方を用いて、ひとつの「オブジェクト」として、共通に使われるデータを共有します。オブジェクトは複数作成できます。また、完璧なひとつのオブジェクトを作るのではなく、オブジェクト同士でインターフェースを使って連携させることもあります。
オブジェクト指向も参照のこと。
2023.03.18-19
プログラミングの基本は、データの保持とビュー画面への表示です。
ビュー画面にデータを表示しながら、その裏でデータ要素を保持し、データとビュー画面の再描画や同期を行うことで、ペイントソフトであっても、Webブラウザであっても、あるいはゲームやモバイルアプリであっても、どんなアプリケーションでも開発できます。
ですが、それだけだと、アプリケーションを終了した時にデータが失われてしまいます。なので、データを永続化するために、ファイルの読み書きやデータベースへのデータの格納と参照を行います。
プログラミングは、一見高度なアルゴリズム処理をやっているように見えますが、それは一部であり、基本的に必要なのはビュー画面に項目を表示しながら裏でデータを保持することです。MFCやMVCフレームワークなどはそのようなことを助ける基盤を提供します。
2023.09.10
X(旧ツイッター)で、「リーダブルコードを読んだことがないやつはプログラマとして論外」とするポスト(ツイート)が話題になっています。
ですが、僕が思うに、本当にプログラマとして優秀になりたいのであれば、そういう権威ある本は読んだほうがいいです。
たとえば、以下のリンクでは、プログラマとして読むことがおすすめの名著が掲載されています。
あるいは、MINIX本の巻末にある参考文献には、OSやカーネルを作るための重要な参考となる書籍が掲載されています。
もっと一般的な本が知りたいなら、「ふつうのLinuxプログラミング」の第18章にある「本書を読み終えたあとは」が参考になります。
そのように、プログラミングの世界には、読むことが必須であるような「名著」がたくさんあります。
いつまでも、初心者向けの言語やフレームワークの入門書だけを読んで、分かった気になっていても、一流のLinuxハッカーになることはできません。
本当に優れた優秀なエンジニアは、そのような「名著」を読んでいます。なので、そのような権威ある名著を読むことで、一流のLinuxハッカーになることができます。
2023.09.11
プログラミングにおいては、「OSやシステムの仕様を学ぶこと」と、「与えられた条件から目的の機能を作ること」の、両方が必要です。
OSやシステムの仕様とは、たとえばUNIXのAPIやC/C++言語の言語・ライブラリAPIなどの仕様のことです。
与えられた条件から機能を作るとは、たとえばネットワーク接続環境において、どのようにしてリモート環境からコマンドを実行するようなシェルを開発するか、といった方法論のことです。
プログラミングにおいては、そのような、「仕様に習熟しておくこと」と、「やるべき機能に応じてどのように作ればいいかという方法論を知っておくこと」の二つが必要となります。
これはすべての場合について言えます。UNIXのAPIだけではなく、XlibやGTKやQtのようなライブラリのAPIを知っておくことも重要です。また、ゼロから機能を作る上で、すべてを自分で考えて作るのではなく、「掲示板の作り方のセオリー」や「コンパイラの作り方のセオリー」のように、セオリー通りに作る方法を知っておくことも必要となるでしょう。
この二つの方法が合わさって、はじめてプログラミングのエキスパートになれるのです。
SSHの開発とUNIXプログラミングについては、SSHも参照のこと。
2024.06.18
結局、プログラミングとは何か。プログラムを作ることで、何が出来るのか。
その答えは、「情報処理」です。
プログラミングとは、情報に対して処理を行うということです。
情報とは、数値、文字、データ、入出力、ヒューマンインターフェース、視覚や聴覚などのマルチメディアを操作する、ということです。
コンピュータに与えるものは情報であり、処理するのも情報であり、人間に対して結果を返すのも情報です。
コンピュータ科学のことを、「情報工学」ということがあります。これは、「情報をやり取りするということがコンピュータ科学だから」という意味だと僕は思います。
これは何も、デスクトップやラップトップの場合だけではありません。たとえば体重計に供えられたICチップや、組み込みコンピュータの場合でも同じです。情報を与えられて、計算やデータ処理を行い、その結果となる情報を返す、これがコンピュータの基本です。
また、情報は単純にローカルにあるとは限りません。情報がメモリにあるか、ストレージにあるか、ネットワーク上にあるかは分かりません。このような「どこかにあるデータ情報」をやり取りすることがまず第一です。
そして、プログラミングとは何なのか。それは、「情報をやり取りする過程となるプロセスやロジックを書くこと」です。すなわち、プログラムとは「この情報をこのように処理します」という決まり事なのです。
プログラミング言語とは、すなわち「情報のやり取りを記述する記法的な道具」であると言えます。
コンピュータ科学も参照のこと。
WindowsやMacのようなGUIのOS環境は、書類をファイルとして保存し、アプリケーションから編集する、という「人間の視覚に訴えかける」インターフェースを採用しました。これにインターネットなどのネットワーク機器が合わさって、日常でニュースの取得やメッセージのやり取りもできるようになりました。
コマンドで操作する従来のUNIXのようなOSは、これに対して、自動化やバッチ処理が容易であるという特徴がありますが、GUI操作であっても、最近では、RPAのように、人間がする操作をロボティックに自動化することもできます。
このようなOSの進歩と同時に、プログラミング言語の進歩も考えられます。
アセンブラでは、機械語をそのままニーモニックにすることで、人間が読みやすくし、計算機としての(電卓+記憶的な)単純さが残っています。
FORTRANやCOBOLでは、より人間の記述しやすい形で抽象化され、人間の読みやすい変数定義や条件分岐などを使うことができ、フォーマット入出力のようなことを簡単にできる機能がありますが、行番号を使ったgotoなど記述が原始的なところがあります。
Cなどでは、記述がより構造化し、ネストされたブロック(ALGOLに由来する)による条件分岐・繰り返し文や関数やサブルーチンを使うことができますが、C++に比べると、オブジェクト指向プログラミングの機能がないか劣っています。
C++では、Cに対してクラスベースのオブジェクト指向を用いることができますが、ガーベッジコレクションなど、速度や効率を犠牲にする重要なオブジェクト指向の機能がありませんでした(少なくとも長い間。今ではスマートポインタがある)。また、C++の仕様は、とても巨大かつ難解です。
これに対して、C++をすっきりとさせながらオブジェクト指向言語として使いやすいものにしたのがJavaです。C/C++では長い間プラットフォーム依存だったマルチスレッドの機能も、Javaでは標準で用意されました。
また、もっと手軽で書きやすい、少ない行で書くことのできるスクリプト言語としては、Perl, PHP, Python, Ruby, JavaScriptなどがあります。
また、そのような手順を手続きとして書き記す、手続き型言語とは一線を画す、より数学的かつ抽象的であることで知られる関数型言語としてはLispやHaskellなどがあります。
このように、OSとプログラミング言語は少しずつ進歩・発展しました。しかしながら、「情報を人間から受け取り、情報を処理し、情報を人間に返す」ということは変わっていません。
プログラミング言語入門も参照のこと。
プログラムは、書いた通りにしか動きません。
バグのない正しいコードは、バグのない正しい動き方をします。バグのある間違ったコードは、バグのある間違った動き方をします。
決して、正しいコードが間違った動き方をしたり、間違ったコードが正しい動き方をすることはありません。
ですが、テストが大切です。なぜなら、「自分の少ない実例では正しい動き方をしているように見えても、さまざまな実例においては想定外のバグがでる」ことがあるからです。
実際のところ、コンパイルエラーでコンパイル・実行ができないのは、まだ間違いとしては可愛いもので、コンパイルが通って実行もできるが、それでバグがあるのが一番困ります。
また、セキュリティには「公開の原則」があります。すなわち、セキュリティを確保するためにコードを非公開にするのではなく、コードをあえて公開して万人の目に見えるようにした上で、みんなでバグを発見して直すことでソフトウェアは正常に動くことが可能となります。
また、公開の利点はもうひとつあります。それが「どこに問題があるか特定できる」ことです。非公開のOSは、もし問題がOS側にあったとしても分かりません。いつまで探しても、どこにも問題がなければ、OSのバグかもしれません。公開することで、どこに問題があるか特定できます。すべてのソースコードがオープンならば、すべてのコードをしらみつぶしに見ればどこに問題があるかを特定できるのです。
プログラミングだけではなく、デザインや執筆についても言えることとして、「ある程度のプロトタイプ」を作ってそれを改良する、ということが有効です。
たとえば、ブログを作るのであれば、最初のうちは何の機能もない、最低限投稿と一覧ができるだけのブログで構いません。
最低限の機能が作れた段階で、「そこまでで終わり」にし、また、思いついたらそのプログラムを改良していけばいいのです。
プログラムについて言えることは、「データがあってプログラムがある」ということです。
外部にデータファイルがあって、それと対応するプログラムがあります。プログラムの中ではデータに対する一般的な処理(手続き)を記述します。変数などは、プログラムがプログラムの中で一時的に保持する記憶です。
また、プログラムについてもうひとつ言えることは、「自動の実行処理」であるということです。テキスト処理であっても、ネットワーク処理であっても、GUIのプログラムやビューであっても、すべては汎用的な自動処理、すなわち手続きであると言えます。
このように、プログラミングを行う上では、データとプログラムを対応付けし、データに対してプログラムが一般的手続きを実行すること、そしてすべてのプログラムは自動処理であることを分かっておくといいでしょう。
コードとファイルも参照のこと。
簡単に言えば、コンピュータのハードウェアは、1か0かのビット情報を演算していますが、人間であるプログラマは、この情報処理の命令の構築を考えます。
ビット情報とは、たとえば8ビットなら、00001111のように、1か0かの1ビットの情報を、桁の分だけ(ここでは8ビット)持ち、これによって数値や文字、計算命令を表したり、ビット列を変更することで値に対する計算や記憶を行う、ということです。
CPUやメモリなどのコンピュータのハードウェアは、このビット情報を演算することができます。8ビットの情報が扱いやすいため、8ビットを1バイトとします。
ハードウェアがビット情報を計算しているとして、人間であるプログラマは何をしているかというと、「命令の構築」、すなわち「どのような命令文を組み立てるか」ということをしています。
プログラマは、命令が処理される「順番」がどのように変化するか、すなわち、命令をどのように構築し、どのような計算をさせることで、さまざまな便利な機能を計算機を用いて実現するか、ということをしています。
最近のコンピュータは、高水準のプログラミング言語や、すべてのプログラムに必要な基本的処理を行うOSが付属していますが、基本的にそれらも含めて、ビット演算をハードウェアが行い、その命令文をプログラマが記述する、ということは変わりません。
しかしながら、なかなか機械語やアセンブリ言語で計算内容を組み立てるのは至難の業です。アセンブラが書ける人はかっこいいですが、普通のエンジニアはC/C++やPythonなどの「高水準言語」を使って命令文を組み立てることになります。この命令文が、コンパイラ言語であれば機械語に変換されて実行されます。あくまで、命令文をもっと人間の分かりやすい言語で書く仕組みであり、命令文を書かなくてもいいわけではありません。
結局のところ、コンポーネントが開発できることが、プログラマにとっての理想かもしれません。
プログラマにとって、ソフトウェアは大きく、カーネルと、それ以外のユーザーランドのソフトウェアに分かれます。
外部から見て、アプリケーションソフトは高度かつ巨大に見えますが、実際は言語とOSの機能を使うだけであり、基本的にはカーネルを使うだけです。
これに対して、カーネルは、外部からは見えませんが、内部ではほとんどすべてをやっています。
ですが、カーネルや言語のような低レベルシステムは、低レベルすぎるため、自分で書く必要はありません。
C#のWindows.Formsのような高レベルな言語は、高レベルであるため生産性が高く、むしろ簡単に作れ過ぎて、実際の技術的なスキルとしては何もしていないだけになってしまいます。
このような中で、もし、ひとつのプログラマの主戦場をあげるとしたら、たとえばWebブラウザのFirefoxのような、大きなアプリケーションソフトを書くことが挙げられるでしょう。
しかしながら、このようなアプリケーション開発では、使うことと作ることは同じです。Firefoxを作るために必要なのは、さまざまな技術を使うこと、あるいはさらに低レベルなシステムを使ってその技術を独自に開発することです。
逆に、Firefoxを作ることよりも、使うことのほうが賢いかもしれません。インフラは、作るだけではなく、使って何かをすべきです。
このようなことは、RubyやPythonのようなサーバーサイドのWeb技術について言えます。
結局、カーネルだけが例外的に賢いだけにすぎず、C#のWindows.Formsエンジニアは、何もしていないように見えて、普通のプログラムを書いている一般的なまともなエンジニアなのです。PHPやRubyのエンジニアも同じで、言語とOSの機能を使うだけで、実質的に何もしていないからといって、自分を卑下する必要はありません。
なぜなら、カーネルや低レベルシステムが例外的に賢いだけにすぎず、それらを自分で書くことは困難を極めるか、多くの場合不必要だからです。MS製品やオープンソース製品を使うことは悪いことではありません。
こうしてみた時、理想とはなんでしょうか。それは、たとえばDelphiやVisual C++やC#などを用いて、Windowsのコンポーネントが作れたりすること、あるいは、RubyのgemsやPythonのモジュールが書けることではないでしょうか。
たとえば、Windowsには、低レベルなOSだけではなく、さまざまなコンポーネントがあります。たとえば、IEコンポーネントなどがその例として言えます。そして、IEコンポーネントは、COMコンポーネントの集合体として作られています。
Windowsだけではなく、GNOMEなどでも、再利用可能なコンポーネントは重視されます。GNOMEの創始者ミゲル・デ・イカザ氏などは、UNIXにおける再利用可能なコンポーネントの重要性をGNOMEとCORBAを論じる論文の中で主張しました。
そう、結局のところ、プログラミングで重要かつ賢いのは、コンポーネントを書くことであると僕は思います。そして、Windowsにおいては、ActiveXとCOMでコンポーネントが開発できること、それが理想ではないかと僕は思うのです。
もしこれがWindowsでない場合、Webであればフレームワークの開発、あるいは言語に近い部分ではgemsなどのモジュールの開発となるでしょう。プログラミングの練習をしたいなら、そういうところから始めていくといいでしょう。優れたgemsは実際に重宝されますし、フレームワークはまだまだ革新的な発想を実現する余地があると思います。
保守性も参照のこと。
僕は本格的なプログラミングの経験がまだまだ浅いので、なんとも言えませんが、プログラミングのコツは、コアのモジュールやルーチンを注意深く設計することにあると思います。
再利用可能な、汎用的かつ一般的なコアのルーチンを書いてしまえば、あとはそれを再利用するだけで、プログラミングは可能です。
コアの部分はほかの雑多な部分から呼ばれるため、コアの部分を注意深く設計してテストすれば、全体の安定性や効率性を高めることができます。
また、クラスにはそれぞれひとつのことをきちんとできる機能を作ります。そして、クラス間のやり取りを考え、それをクラスライブラリにします。
さまざまな流儀はありますが、僕はRailsのように、規定となる抽象的な関係性をまず築いておいて、それを継承して具体的な機能を作っていくように、階層的に低水準のレイヤーから高水準のレイヤーに肉付けしていく方法が一般的でいいと思います。
低水準のレベルで基本的なAPI構造を構築し、アプリケーションはそれを踏まえた上でそれに機能を追加して実装するように作ればいいと思います。
このような手法はデザインパターンの一種として、Template Methodパターンとして知られています。
PythonやRubyなどの言語を使う上で、言語について学び終えた入門者が、プログラムを書く上でするべきことは、プログラムの内容を考えることです。
ですが、プログラムには、「その問題固有の解決方法を記述する」ことと、「汎用的に使えるルーチンを記述する」ということの2つがあると思います。
問題を解決する上で、問題は小部分に分けて考えられます。そして、部分を分けた時に必ず生まれるのが、「この問題を解決するためだけではなく、幅広く多くのことに活用できるような汎用ルーチン」の実装です。
このようなルーチンは、それが専門的な計算などのロジックでなく、一般的なアルゴリズムやデータ構造であれば、言語や標準モジュールに、最初から用意されていることも多いです。多くの場合、言語に付属の標準モジュールを使うことで、このような汎用ルーチンを自分で実装することなく利用することができます。
このような標準の汎用ルーチンの使い方を知ることは、プログラミングに有益ですが、すべてのAPIを暗記することはできません。なので、最低限必要なもの以外は、その都度Googleなどで検索して調べて使うことになります。
そして、汎用ルーチンがもしなかったとしても、諦めてはいけません。そのルーチンを自分で作ればいいのです。
言語における標準のAPIは、汎用的ではありますが、単純なAPIが多く、それを使うだけでは目的の問題が解決できないようなことは多々あります。
このような場合に、諦めるという選択肢はよくありません。なぜなら、JavaやPythonのようなオブジェクト指向言語は、そもそもクラスの継承などを行うことで、標準で存在しない機能の拡張を自分で開発することができるからです。
よって、標準のAPIでできないことがあっても、諦めることなく、「もともとのクラスを利用して自分で継承して作る」という姿勢を持つといいでしょう。
システムエンジニアの仕事は、「問題を解決すること」です。
まず、要求されている問題を明確に定義します。顧客の要求を考えたり、実際の現場から何が問題として求められているのかを考えます。
そして、どのようなシステムを構築すれば問題を適切に解決できるかを明確にします。
次に、そのようなシステムをどのようにすれば構築できるかを考えます。
その後に、システムの設計を行い、設計からプログラムを実装し、コードを書きます。
そして、テストし、納品し、システムの保守を行います。
実際のところ「コードを書いている時間よりも、考えている時間の方が多い」とよく言われます。
また、プログラミングとは、「問題を解決する」ことそのものです。どのように計算し、操作し、処理し、実行すれば問題の「ソリューション」(解決方法、答え)を与えられるかを考えるのです。
プログラミングにおいては、「数値の変換」という発想がよく起きる。
たとえば、「入力された文字を人数分表示する」というプログラムがあったとして、ここでは、人数が表示回数に変換されている。
人数が3人居たとして、その3人がそのまま3回になる。このように、「ある数字が別の数字の元になる」。10人居れば10回になる。3×10という単純な計算を、プログラミングではそのように行う。それぞれの人数だけ行うように、それぞれの数を別の数にする。あるいは、条件式や分岐にすることもある。
プログラムを開発するコツは、「プログラムに必要な機能をよく分析すること」です。
たとえば、テキストエディタを作るのであれば、バッファに文字列を格納し、テキストエリアに整形して表示する機能がまず必要です。バッファが更新されたら、ビューも更新しなければいけません。
また、ファイルから読み込む機能、ファイルに書き込む機能も必要です。
また、検索や置換、あるいは比較のような機能があると、テキストエディタらしくなるでしょう。テキストエリアには、キーワードを色分けする機能(シンタックスハイライト)があると良いでしょう。
機能を分類できたら、今度はどのようなクラス設計でそれを実現するかを考えます。まず、バッファをメンバ変数としたエディタークラスを作ります。同時に、シンタックスハイライトとビューの常時更新機能をつけたテキストエリアコンポーネントを作ります。同時に、ファイルを管理する開く・保存機能を作ります。最後に、検索と置換の関数を作ります。これらをまとめて、エディタークラスに実装します。
常時更新機能を実装するには、バッファを編集するのに専用のインターフェースを設けて、テキストエディタにビューを更新する関数を作り、バッファのインターフェースが呼び出されたら、必ずその更新関数が呼び出されるようにします。この更新関数はどこからでも呼び出せるようにして、ファイルを開いたり保存したり、設定を変えた時にも呼び出します。また、シンタックスハイライトを作るには、テキストエディタコンポーネントが表示する処理を行う時に、バッファ読み込みとビューの表示の間に介入して、ひとつひとつのキーワードを解析し、その情報をテキストにタグのような形で付け足して、表示する処理の部分で、テキストに付属された情報の通り、テキストエリアで文字色を変えてフォントを表示するようにします。
そのように、どんな機能が必要かを考えることで、クラスの設計もしやすくなります。
プログラミングを行うよりも、手動で操作した方が簡単にできることは確かにあります。
たとえば、テキストエディタで検索・置換を行ったり、行ごとにカウントして処理したりすることは、確かに手動でも行えます。
しかしながら、プログラムの真価は再利用できることにあります。一度自動化してしまえば、あとは何度でも再利用できるのです。
そのため、テキストエディタを使うところをRubyを使うようにするだけで、作業ははるかに楽になります。たとえば、ファイル一覧を得るPHP/Rubyなどの関数であるglob()などを使うことで、ひとつのファイルだけではなく、多くのファイルを簡単に操作することができます。
プログラミングを行う上で必要なのは、構成や設計を行う能力です。
たとえば、オンラインショッピングサイトを作るような「やり方」を学ぶことで、学ぶだけではなく実務的なプログラミングができるようになります。
これについては、Railsのようなフレームワークで実際のやり方を学んだり、本を読んでやり方を学ぶこともできますが、以下に挙げるプログラミングスクールに通うのもおすすめです。
方法として言えるのは、リナックスアカデミーや侍エンジニア塾やGEEKJOBやプログラマカレッジなどのプログラミングスクールに通うことです。
以下にリナックスアカデミーの評判があるので参考にしてください。
授業料は高いですがリナックスアカデミーの評価はおおむね良しです。お金があるならおすすめします。
また、プログラミングについて言えるのは、「アーキテクチャ概念の構築」です。
たとえば、MozillaのXULや、RailsのMVCフレームワークなどがこれに当たります。
プログラミングは、単に作るものの枠組みを考えてコードを書くだけの作業ではありません。自分の考えたアーキテクチャ概念を「発明」するという仕事なのです。
ですので、ただのコードを書くだけの「コーダー」と呼ばれるエンジニアではなく、アーキテクチャを創造する「アーキテクト」になってください。アーキテクチャを創造するためには、プログラムの全体像の設計や構成ができることに加えて、必要となる技術やプラットフォームやシステムデザインに精通し、プログラミング以外の能力も必要になるでしょう。
色んなやり方や色んなプログラムがあるため、一概に「このように開発しろ」とは言わないが、アプリケーションの設計と実装の手順の一例として、以下が参考になるかもしれない。
まず、どのような処理が必要になるか、明確化する。
・データ(あるいはデータ属性)をどのように保管し、参照し、読み書きするのか?ビューの表示機能にどのようにデータを与えるのか?そのためにどのように変換・保持するのか?
・ビューをどのように表示し、レイアウトや再描画をどのようにするのか?ユーザーは表示されたアートワークをどのように(マウスやボタンによって)操作するのか?
・ユーザーが操作するインターフェース(UI)は、どのようなものにし、どのように表示・参照・変更・適用するのか?どのようなフォームやパネルやダイアログを表示するのか?
・プログラム自体の設定は、どのように保持・参照・変更・適用するのか?プログラムの処理の中でどのように設定を適用するのか?
・プログラムの主要な機能は何か?
・プログラムの主要な機能をどのように実現し、実装するのか?
・機能の呼び出しインターフェース(API)は、どのような規約(ルール)に基づいて構築するのか?
・APIに基づいて、機能をどのように分割・相互参照・モジュール化するのか?
・プログラムの開発のベースとして、どのような技術を採用するのか?(コンソール、Windows、Web、フレームワークなど)
・どのようなプログラムにしたいのか?(多機能、軽量、簡単、プロ仕様など)
また、こうして出てきた「漠然とした全体像」を、明確に、それ以上細分化できないところまで細分化し、決まっていないことが何もないぐらい明確化する。
そして、明確化した内容を、実際のプログラムにするために、「クラス」と呼ばれる単位に分割し、クラスとクラスの関係性を記述する「クラス図」と処理の流れを記述する「シーケンス図」を描く。
最後に、これらに基づいてコードを書く。ここで、初めて、プログラミング言語の知識の内容を活かすことができる。
後日注記:実際のところ、上記はMFCのドキュメント・ビューのような場合に限られる。もっとより一般的には、「処理の流れをどうするか」「内部的な設定情報をどのように保持するか」「ライブラリや再利用可能なコンポーネントをどのように利用・構築するか」などといった具合になる。
Qiitaでエンジニア向けの読み物系ページをまとめていらっしゃる方がおられます。とても参考になります。
後日注記:また、Qiitaで、良いコードの書き方をまとめていらっしゃる方がおられます。とても勉強になります。
日本人の科学者である嶋正利とIntelが一緒になって作ったIntel 4004以降の「マイクロプロセッサ」では、CPUは基本的な演算機能だけを提供し、あとの多くの機能を「プログラミング」によって成り立たせる、という新しい考え方を作りました。
これは、今のプログラム開発についても当てはまります。基本のCPUの機能は少なく、「原則プログラミングで機能を増やしていく」という発想です。
WebブラウザのSleipnirのように、WindowsのGUIアプリケーションには、こうした考え方が色濃くあります。原則、プログラミングとは、「プログラミングによって付け足せる機能をどんどん付け足すこと」に他なりません。CPUのレベルで、プログラミングは機能をいくらでもソフトウェアで付け足していく、ということなのです。
古いコンピュータ(2.パソコン)も参照のこと。
先に「プログラミングスクール」について書きましたが、Udemyという有料ながら良質なプログラミング入門コンテンツ(動画講座)を配信しているサイトがあります。
特に、以下の人はUdemyを「非常に安い買い物だった」と言っています。
実際、プログラミングスクールに通うには高額な授業料が必要で、コスパが悪いです。Udemyの動画は内容が詳しく、内容に比べて安いのだと思います。
プログラミングを行う上で、使う単語の英語的な意味を知っておくことはいいことかもしれません。
たとえば、変数を表すvariableには「変わりやすい」「変異する」という意味があります。
また、関数を表すfunctionには「機能」「働き」という意味があります。
同様に、objectには「もの」、classには「階級」、instanceには「実例」「事例」「事実」という意味があります。
このような英語の意味を知っておくことで、使う単語の「どういう目的でこのキーワードを使うのか」という意味が分かります。
Intel系のCPUは、32bit(IA-32)ならx86(i386, i486, i586, i686)となり、それ以降はItaniumなどのIA-64とx86互換のAMD64(x86_64)となります。
他に、携帯デバイスとして使われるARMや、昔のMacで使われていたIBMのPowerPC、Sunの開発していたSPARCなどがあります。
歴史的にUNIXはC言語で書かれたことから、移植性が高いと言われるが、オープンソース系UNIXでは、NetBSDの移植性が高いことで有名だ。
Linuxもさまざまなアーキテクチャに移植されているが、移植にはLinuxカーネル、Glibc、GCCなどの対応が必要(僕も詳しくは知らない。LLVMやNetBSDのlibcを使うこともあるらしい。)なことから、新しいCPUアーキテクチャに対応するのは簡単ではない。
Debian GNU/Linuxは、さまざまなアーキテクチャに向けたバイナリパッケージを公式で配布していることで有名だ。
Gentoo Linuxは、ソースベースのパッケージ管理システムを採用することで、さまざまなCPUアーキテクチャに対応している。だが、ほとんどのパッケージでコンパイルが必要だ。
Fedoraなどのメジャーなディストリビューションも、ARMには対応する傾向にある。
いずれにせよ、Intel以外のCPUアーキテクチャでLinuxなどを使う場合は、自分で問題に対処出来る問題解決能力が必要だ。
移植性やCPUアーキテクチャも参照のこと。
Linux設計やLinux システムコール・APIにいろいろと書いています。参照してください。
コンパイラ開発を参照のこと。
ある意味、プログラミングなんて、ただ使うだけだ、ということも出来る。
たとえば、ホームページをHTMLで書いていたとして、日記の月別一覧を色んなところに張り付けたい場合は、Rubyなら
2017.11.01 2017.11.02 2017.11.04 2017.11.05というテキストファイルを作っておいて、その上で
File.open("diary_2017.11.txt") do |io| while line = io.gets line.chomp! puts "<p><a href=\"" + line + ".html\">" + line + "</a></p>" end end
とRubyでいくらか書いてやるだけで、簡単に動くRubyのCGIファイルを作ることが出来る。日付を追加する時はdiary_2017.11.txtだけを編集すれば良い。
だから、何をするにしても、プログラミングはただ使うだけだ。自分で何も作っていない。使うだけに終始する、それがプログラミングなのだ。
上のコードを綺麗な関数にすると以下のようになる。
def diary_list(path) File.open(path) do |io| while line = io.gets line.chomp! puts "<p><a href=\"" + line + ".html\">" + line + "</a></p>" end end end diary_list("diary_2017.09.txt") diary_list("diary_2017.10.txt") diary_list("diary_2017.11.txt")
また、オブジェクト指向でなぜ作るのか、という話が分からない人が多いが、フィールドは視点を変えてみるとグローバル変数と似ている。クラスの中だけで使われるグローバル変数だ。そういう風に考えると、JavaやRubyのカプセル化という発想も良く分かると思う。
ワープロを作るために必要なのは、
・データの保管と読み書きをするデータオブジェクト
・文字や画像を表示する配置オブジェクト
・データオブジェクトと配置オブジェクトの変換をするメソッド
・配置オブジェクトを表示するメソッドとビュー
・ビューを操作した時に配置オブジェクトとビューを更新する描画モード
・配置オブジェクトの編集機能
ではないかと思う。
(2018-01-24より引用。)
きっと、ブラウザなども同じ要領で作れる。MFCにはドキュメント・ビューという概念が存在するが、僕はそれには詳しくない。
基本的に、配置メソッドは四角形の領域を表示するようにして、表示メソッドによってビューに「全ての配置オブジェクトを表示する」機能をつけて、描画モードによって操作に応じて部分的に配置オブジェクトの表示を更新する機能をつける。そして、保存する時はデータオブジェクトに変換すれば良い。
オブジェクト指向は、クラスによってデータとメソッドを一体化させ、その上でメモリの管理をオブジェクト単位で行い、オブジェクトを簡単に作成・削除し、グローバル変数のようにクラス内部のメソッドからオブジェクトのメンバ関数を処理するものだ、と考えると良く分かる。
2018-02-27より。
僕は、出来ればプロジェクトマネジメントの研究をしたい。特に、グーグルの戦略と働き方に興味がある。あるいは、マイクロソフトの働き方に興味がある。そして、国連の活動に興味がある。先日は、国連の広報ブログを読んでいた。
プログラムとは、手順書だ。パソコンをアセンブリから見ると、ただの電卓だ。プログラムとは計算の手順であり、コンパイラは計算の手順をより基本的なものに変換する。プログラムを実現するのはカーネルだが、ファイルシステムなどは本来は必要ない。システムを具体的に実現するのがソフトウェアだ。
プログラミングとは、手順書を書くことでシステムのインフラ基盤を作る作業だ。
やることが定まっている全てのタスクは、きちんとシステムを作れば簡単に実現できる。それがプログラマだ。
普通、描画システムぐらい簡単に作れる。ブラウザではレンダリングエンジンと言う。一つ一つのシステムを作って、細分化させながら組み合わせれば、きっとIllustratorも作れる。
プログラミングは、ある意味自動実行とリクエストだ。だから、サーバーをやるとできるようになる。
コンパイラは、トークンを解析するシステム、文法を成り立たせるシステム、実行内容を作るシステム、アセンブラを吐き出すシステムを作れば作れる。
コンパイラは決して難しくない。機能と構文ごとにシステムを作れば作れる。Rustなら、借用やトレイトの解釈システムと実行システムを作れば作れる。
僕は、GUIプログラミングには主に3つの方法があると思っている。
まず、Windowsのメッセージループや、X11のイベントループ、Swingのリスナのように、「メッセージループ」を行うことでイベントを分類する方式。
次に、MFCのような、継承とオーバーライドでオブジェクト指向でメソッドを書き換える方式。
そして、GTK+やVBのような、ウィジェットにコールバック関数でイベントドリブンを行う方式。
どの方式も一長一短があるが、少しずつレベルと抽象度が高くなっていく、と考えると良いだろう。理想はGTK+やVBだ。
パソコンの初心者が勘違いしやすいこととして、「パソコンの処理は、画面に出てくるウィンドウがやっている」ということがある。
本当は、ウィンドウはただやりとりを表示しているだけで、実際の処理はCPUとハードウェアがやっている。
ある意味、「実行ファイルがやっている」という考え方はおかしくはない。それは、OSが実行ファイルを実行する、という発想から生まれている。だが、本当の処理はCPUがやっている。画面に出てこない色々なことを全て、CPUがプログラムコードを実行してやっている。そのことを覚えておくと、パソコンが長い間停止して何をやっているのか分からない、という時でも、「ああ、CPUは今頑張っているのだな」ということが分かる。プログラムとは何なのか、コードはどんなことを書いてどんなことを実行させているのか、良く分かるようになる。
2017-10-28、2018-03-01に関連する内容があります。
プログラムを作るセオリーとして、オブジェクト指向のやり方が良く使われます。それは、「共有されるデータを変数に格納し、関数を作って呼び出す」ということです。
多くの場合、これでプログラムは書けます。具体的には、下のカーナビのシステムが参考になるかもしれません。
たとえば、カーナビのシステムを作る場合。まずは、地図データがあります。この地図データを、まず、変数に格納します。
そして、機能を作る前に、マッピングとして、地図データの基本的な操作メソッドと、ディスプレイやモニター画面に地図を表示する関数群(API)を作ります。
そして、基本的な地図の機能、たとえば拡大・縮小、回転、移動、のような機能を作ります。これは画像的なライブラリを使うと良いでしょう。
地図を解析して、道順を知るための機能は、まず、膨大な地図データをデータベースに登録します。そのために、画像から自動で地図データの「属性」を取得し、自動で登録する解析プログラムを作ります。
このデータには、道路の長さと、必要な移動時間、そして道路の右端と左端の位置情報や、道の分岐の情報を登録します。
そして、現在地と目的地の位置情報から、「どんな道のりのパターンがあるか」を、道路の右端と左端の位置情報や分岐の情報を上手く繋ぎ合わせることで、全パターン洗い出します。
そして、それぞれのパターンにかかる時間を計算し、それをソートして完成です。
その上で、さまざまなナビゲーションに必要な機能(道のりを別の色で表示したり、状況に応じてナビゲーションする機能)をつけて、カーナビのシステムを作ることができます。
このような場合にも、必要なのは、「データを保持する部分と、そのデータを操作する関数の部分を作ること」です。
さらに言えば、「どの道からどの道に繋がるか」というリンク情報をデータベースに登録しておくと、あとあと便利かもしれません。また、さまざまなボタンやメニューを作って、イベント駆動のメソッドを書く必要があるでしょう。そして、きちんと動くかを確認するために、テスト関数のようなものを用意して、さまざまな条件と結果をチェックするようにしましょう。現在位置を取得するGPS機能や、現在位置の変化とともに画面を移動させていく「リアルタイム解析機能」も必要になるでしょう。
みんな、Webブラウザのようなものは難しすぎて誰にも作れないのだろうと考えているかもしれないが、意外と簡単である。
まず、HTMLを装飾と文字データの属性を持ったレイアウト情報(レイアウト文字データ)に変換する。ここでCSSを考慮しても良いだろう。そして、与えられたオプションから四角形の領域を表示する部分を実装する。つまり、「divとspanだけを作れば作れる」からである。
pタグやh1タグのようなものは、全部divとspanを使って作れば良い。
レイアウト部分が作れたら、次は装飾部分を作る。まず、文字を表示しなければならない。次に、文字と背景と線に色をつけなければならない。
最初からCSSを考慮した設計にすると、あとで楽が出来るだろう。
また、その上で、画像を表示したり、さまざまな機能を作ったりする必要はある。JavaScriptの実装も難しいし、そのためにはDOMを実装しないといけない。
だが、これくらいの、HTMLをパースして領域を表示する部分は、むしろ、僕のロボット(人工知能)のプログラムを参考にすると上手く作れるだろう。HTMLを読み込んで、CSSを解釈し、タグをパースし、領域とレイアウトを決め、装飾をつけて、最終的にモニターに表示するのを、ひとつの制御の流れとして実装すれば、あとはオプションを解釈して動作を変えるだけで、おそらく作れるだろう。
本当はコントロールを作るのが難しいのだが、テキストエディタは既にあるコントロールを継承して改良することで簡単に作ることができる。
驚き最小の原則とは、特にPerlなどのプログラミング言語などの設計に言われる考え方で、「みんなが驚いたり勘違いしたりすることを極力なくして、当然そのように発想するように設計しよう」という設計の考え方。
車輪の再発明とは、一度作った基本的なベースシステムをもう一度作り直す(再発明)のではなく、出来るだけ共有して再利用しよう、という考え方。UNIXなどの他、Windowsなどでも言われる。
後日注記:実際のところ、「絶対に動くサブルーチン」を作ってしまえば、あとはそのサブルーチンや関数を再利用するだけで絶対に動く。プログラミングではそういう発想をする。同じ処理を別々の場所に何度も書いていると、後で直すのにも時間がかかる。
後日注記:車輪の最発明は、このようにネガティブにとられることもある一方で、学習や習得に対しては、ポジティブに「積極的に車輪の最発明をしよう」と言われることもあります。既にあるプログラムと同じものを作ることで、プログラミングの勉強や習得に結びつきます。たとえば、Web系ならブログエンジンや掲示板、システム系ならターミナルエミュレータのようなものを「車輪の最発明」して作ることで、スキルアップに繋がります。
Keep it simple, stupid.(シンプルを保て、愚か者め)とは、UNIXで言われる考え方で、「シンプル(単純)」な状態を保っておかなければ、すぐに使い物にならなくなる(設計の間違いを生む)、という考え方。
また、UNIX哲学として「システムのパワーは、プログラム自身からではなくプログラム間の関係から生じる」という考え方が言われている。ひとつのプログラムを使うのではなく、たくさんのプログラムを使って成り立たせるソフトウェアの環境がシステムのパワーを発揮する。特に、findやgrepのような小さなコマンドプログラムを組み合わせて使う、といった時に言われる。個別のプログラムだけがパワーを発揮するのではなく、プログラムをとりまくシェルやOSなどの環境の関係がシステムのパワーを発揮するという、システムエンジニアらしい考え方である。
Ruby on Railsで言われる考え方で、複雑な設定をするよりもある程度の規約をそれぞれが守るようにした方が、フレームワークの管理は簡単になる、という考え方。
Linuxやオープンソースで言われる考え方で、バグがいくらあったとしても、それを修正するための発見者や貢献者の目玉がたくさんある(たくさんの人がコードを検査している)のであれば、バグは深刻ではない、とする考え方。
オンラインでソースコードを開発・コンパイル・実行できるサービスは以下のようなものがあります。
paiza.IOはWeb上でプログラムのソースコードを記述し、オンラインでコンパイル・実行できるサービス。C/C++, Java, Perl, Python, PHP, Rubyなどさまざまな言語に対応している。
AWS Cloud9は本格的なオンライン開発環境。無料の会員登録が必要。