軽く中身を見た。
- 作者: Dmitry Jemerov,Svetlana Isakova,長澤太郎,藤原聖,山本純平,yy_yank
- 出版社/メーカー: マイナビ出版
- 発売日: 2017/10/31
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (2件) を見る
裏を返せば、本書を読み進めるには「一定以上のJavaの経験」が必要であるし、「Kotlinの基礎文法」の学習に関しては他の文書を併用した方が良い。
それなりに人を選ぶ本だ。
軽く中身を見た。
裏を返せば、本書を読み進めるには「一定以上のJavaの経験」が必要であるし、「Kotlinの基礎文法」の学習に関しては他の文書を併用した方が良い。
それなりに人を選ぶ本だ。
組込み開発でのプログラミング言語というとC言語(時にアセンブラ)が真っ先に挙げられる気がするのだが、実務的にどこまで正しいのか書いておこうと思う。
結論から言うと、組込みプログラマの立場としては「組込みプログラミング=C言語」は概ね正しいのだが、組込み業界という視点では「C言語以外も結構使われている」ということになる。
そもそも組込み業界ではどんなソフトウェアが作られているだろうか?
組込み業界と言っても結構広いので、分野によって色々と異なるものだが、私が関わっているのは「ガジェット・スマート家電」寄りの分野だ。作っているソフトウェアは、組込みシステム向け三層アーキテクチャ(ドライバ・ミドルウェア・アプリケーション)が適用される程度には大きい*1。OSはRTOSの類が大半だ。
で、外注も含めて、書かれているソフトウェアは以下のような感じだ。
今の時代、リッチな組込み機器はUSBやネットワーク経由で他のサービスと連携する仕組みを備えている。で、連携先の数だけ必要なソフトウェアが増えている。
接続方式が一般的なプロトコルで十分であるとか、外部デバイス側のフレームワークで提供されている通信機能で十分な性能が得られるような場合は、外部デバイスとのやり取りの部分を仕様で定義した上で、外部デバイス側のソフトウェアの開発を外注することが多い。
例えばWebサービスのサーバサイドが一般的なRESTアーキテクチャのWebアプリケーションで十分ならば、Webアプリケーション側のWeb APIのURLや通信データの形式を決めた上で、Webアプリ専業の会社に発注してしまうのだ。
しかしちょっと特殊なことを実現しようとなると、サーバサイド側の実装も自分たちでコントロールしたくなるので、サーバサイドの初期実装まで自分たちで行うことになる。その後はシステムの性格次第で、運用だけ外注してしまうか、運用と改修を共に外注してしまうことになる。
同じことはモバイルアプリにも言える。機器との接続方法がSDKに用意されている一般的な機能で済むとか、アプリのUIが凝ったものでなくて普通のUI部品で済むようなケースでは、外注することが多い。しかし特殊なことを実現したいとか、UIを凝りに凝ったものにしたいとか、そういう事情がある場合は内製することが多い。
先の一覧でいうなら、(1)〜(3) はほぼ確実に自分たちで開発するが、(4) 以降は開発するシステムの性格次第だ。アプリのデザインや性能要求が絡んでくる場合は、自前のPCアプリ・モバイルアプリ開発部隊を持っていたりする。
実際に組込み機器内で動作するコンポーネントである (1)〜(3) のうち、ステレオタイプの組込みプログラミングに最も違いのは (1) だ。ドライバやファームウェアを担当している人は名実ともに組込みプログラマである。彼らの主要言語はC言語だ。その意味で、組込みプログラマの感覚では「組込みプログラミング=C言語」となる。
一方で (2) のミドルウェア屋は「ハードウェアから分離させた移植性のあるモジュール」を開発するのが主要業務なので、C言語使いではあるものの、組込みプログラマという意識はやや薄い。(3) の組込み機器のアプリケーション層担当ともなると、ミドルウェアやドライバのAPIを叩くのでハードウェアを直接制御することは無いし、LCD表示用のGUIツールキットとの絡みでC++を使うことも多々ある。
なので、組込み業界の中のどの分野かに依存する話ではあるが、組込み系の企業に就職した人が使うプログラミング言語がC言語であるとは限らないし、誰もがハードウェア制御用のコードを書ける訳でもない、という現状がある。
サービス展開も含めて規模の大きなシステムの場合、そのシステムを開発している企業の中で分業化が進んでいるものだ。だから、若手の頃から組込み機器のアプリケーション層の開発をバリバリやってきて、C++ベースのオブジェクト指向プログラミングの手練れになった反面、ハードウェアを直接叩くドライバの開発経験がない――みたいな人が実在したりする。
私自身、キャリアの大半が三層アーキテクチャのミドルウェア層の開発なので、組込みらしいハードウェア制御プログラムを書いたのは2度だけだし、その開発も業界に入ってから10年以上経ってから偶然遭遇したものだった。
ということで、分野を選ぶ必要はあるものの、C言語が使えなくても組込み業界に入ることは可能だ。結構、機器と連携するモバイルアプリ開発とかで、AndroidやiOS向けアプリ開発の手練れを探している企業は多いと思う。
ただし、少し特殊な要件がある場合は、例えばモバイルアプリ開発でもC言語やC++の知識が求められることがある。リアルタイム性に関する性能要件がやや厳しいので核となる部分をCやC++で実装する必要があるとか、機器とのTCP通信にて独自のプロトコルを使う必要があってクライアント側としてCやC++で実装されたモジュールが提供されるので組み込む必要があるとか、そんな感じ。なのでAndroidならNDKの、iOSならC++11のスマートポインタとObjective-C++の知見*2が要求されることになる。
(だから先の一覧のmacOS・iOS・Androidアプリの開発言語にC++を含めている)
結論として、繰り返しになるが、組込みプログラマ的には「組込みプログラミング=C言語」という構図は概ね正しい。しかし組込み業界という俯瞰した視点では「C言語以外も結構使われている」ということになる。業界にはデバイス制御プログラム以外のソフトウェアを書いている人もそれなりにいるのだ。
なお、上記の議論は「製品としてリリースするソフトウェア」を前提としている。開発用の小ツール――内製ツールやプログラマの個人用ツールとしては、テキスト処理に長けたスクリプト言語やExcel制御用の言語(VBAやPowerShell)がごく普通に採用されているものだ。ただ、全般的にC言語やC++(better C)畑の人が多いので、Windows向けデスクトップアプリ開発では(CやC++のモジュールをそのまま組み込むことができる)MFCやQtが選択されやすい傾向にはあると思う。
*1:規模の小さなマイコン・プログラミングだと、せいぜい「ドライバ・アプリケーション」の二層のアーキテクチャであるし、モノによっては明確な分離がされていないごった煮の実装となっている場合もある。
*2:CやC++のモジュールをObjective-C++でラッピングして、インタフェースをObjective-Cとして見せることで、Swiftから楽に使えるようにするのである。その際に、Objective-CのARCと歩調を合わせてリソース解放するように、C++11のスマートポインタを使う。
「ジュニアを採用しない連中はシニアに値しない - portal shit!」を受けて。
そもそも「ジュニア」がどの程度の人を指し示すか、という話はあるが、アメリカの話っぽいので、おそらく最低でもCS(コンピュータ・サイエンス)の学士は持っているだろうし、修士や博士を持っている人もそれなりにいるだろう。
で、「履歴書の順序づけ - The Joel on Software Translation Project」からエスパーすると、アメリカのテック企業はCSの学位そのものの有無に拘っているのではなくて、「学位を持っている=CSの知識を持っている」という判断で履歴書をふるい分けた上で、次の採用ステップ(電話面接など)に進めているのだと思う。日本企業でも、例えば電子機器メーカーの技術者採用でその手の学部卒を採用しているものだが、それと似たようなものだろうか? ただアメリカのテック企業や日本の中小ベンチャー企業の場合、ソフトウェア開発の経験者(それこそ創業者)が履歴書のふるい分けに関わっているなら、CSの学位が無くても特筆すべき事項(例えば有名なOSSのメンテナである等)が書かれていたら履歴書のふるい分けで落とされることは無い――という印象がある(実際のところは不明だが)。
ついでに、「どうしてプログラマに・・・プログラムが書けないのか?(原文)」を読んでアメリカのテック企業でのソフトウェア開発者採用事情を推測するに、流石にプログラムを書けない人はアウトだと思われる。
つまり「ジュニア=CSの知識を持っていて、簡単なコードぐらいは書ける人」である。
「ジュニアを採用しない連中はシニアに値しない」というのは、裏返せばソフトウェア開発者を採用する際の足切り基準がジュニアであると言っているに等しい。
そして、空気を読んで書かれていない行間を推測するなら、ジュニアに満たないノービス*1は考慮に値しない、ということである。CSの学位を持っていないとか、学位を持っていてもFizzBuzzのような簡単なコードすら書けない連中は、そもそもスタートラインに着くことを拒否される可能性が高い(ただし先に挙げた「例えば有名なOSSのメンテナである等」の天才枠は別で、CSの学位が無くても拒否されないだろう)。
あとアメリカの企業の場合、日本の企業よりも「職務不適格による解雇」は行われやすいだろう。
だから、スタートラインに着くことができたジュニアも、ベテランやシニアにステップアップできなければ、少なくとも職業プログラマとしては淘汰されるだろう(もちろん、キャリア開発を経て別分野に転身するなら話は別だが)。
一方で、元記事にもあるように、企業側はジュニアを受け入れてキャリア開発を支援する体制を整える方が健全だろう。誰だって最初は(職業プログラマとしては)素人なのだ。
ところで、ジャパニーズ・トラディショナルな新卒一括採用では、企業によっては「計算機科学とは無縁で、プログラムを書いたこともない人」みたいなノービスがソフトウェア開発者として採用されることがある。採用時に所属学部などでフィルタしている場合も、採用の過程でプログラムを書かせるところは稀だろう。
つまり日本企業では、ソフトウェア開発者としてジュニアに満たないノービスが採用される可能性が(少なくともアメリカのテック企業よりも)高い傾向にある。
あと、「職務不適格による解雇」に値する水準が高く設定されている(でないと解雇の妥当性をめぐる訴訟リスクを抱える)点もアメリカとは異なる。
そういった日本独自の事情を勘案した上で、幾分か割り引いて件の記事を読むべきだろう。
個人的には、ジュニアを採用した場合でも彼/彼女がベテランに育つか否か博打な面があるのに、ジュニアよりも教育コストがかかる――そして心が折れて数年で退職してしまい、それまでかけたコストが水泡に帰する可能性がある――ノービスを採用するのはリスクが大きいと思う。
せめて情報系の学部卒や専門卒に絞り込むとか、もしくは「職業訓練で半年座学と実習を受けながら基本情報技術者を取りました」ぐらいに気合いの入った退路の無い人でないと……教育するにしても、ジュニアとノービスではスタート位置が大きく異なるからなあ。
umask(1)の仕様を調べようとしたのである。
で、ググって見つかった「デフォルトのファイルアクセス権 (umask) (Solaris のシステム管理 (基本編))」を見てちょっと驚いた。
設定する umask の値は、与えたいアクセス権の値を 666 (ファイルの場合) または 777 (ディレクトリの場合) から引きます。引いた残りが umask に使用する値です。たとえば、ファイルのデフォルトモードを 644 (rw-r--r--) に変更したいとします。このとき 666 と 644 の差 022 が umask コマンドの引数として使用する値です。
デフォルトのファイルアクセス権 (umask) (Solaris のシステム管理 (基本編))
「引きます」とか「引いた残りが」とか、引き算をしているかのような記述である。
職業病だと思うのだが、私は `mask' という単語より、ビット演算によるマスクを行っているものと予想していた。しかしこの文書のニュアンスでは、ビットマスクではなく引き算を行っているように読み取れる。
日本語に翻訳する際に微妙な訳になったのかと思ったが、Oracleの別の文書である「umask(1) (man pages section 1: User Commands)」の記述はこんな感じだった。
The value of each specified digit is subtracted from the corresponding ``digit'' specified by the system for the creation of a file (see creat(2)). For example, umask 022 removes write permission for group and other (files normally created with mode 777 become mode 755; files created with mode 666 become mode 644).
umask(1) (man pages section 1: User Commands)
`subtracted' という単語より、私の拙い英語読解力では「引き算している」という感じに読み取れる。
しかし「Umask - ArchWiki」には別の記述がされていた。
新しく作成されたファイルに設定されるパーミッションビットの値は論理非含意 (付加) を使って計算され、論理記号で表現することができます:
R: (D & (~M))
つまり、最終的なパーミッション R はデフォルトのパーミッション D の論理積とファイル作成時のモードマスク M の論理否定が合わさった結果になります。
Umask - ArchWiki
ArchLinuxのドキュメントでは、引き算ではなくビットマスクしているものと読み取れる。
どちらが正しいのだろうか?
POSIXのumask(1)の説明には以下のように「logical complement」とあるので、ビットマスクしているっぽいように思うのだが……。
For a symbolic_mode value, the new value of the file mode creation mask shall be the logical complement of the file permission bits portion of the file mode specified by the symbolic_mode string.
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/umask.html
試しに、挙動を検証する方法を考えてみた。
まず、引き算とビットマスクとで計算結果が異なるケースは存在するだろうか? マスクが0022の場合、引き算とビットマスクとで計算結果は同じだ。しかしマスクが0033の場合、ファイルの場合の既定値(0666)との組み合わせにおいて、計算結果が異なるはずだ。
以下のプログラム(C言語)で確認したところ:
#include <stdio.h> #include <stdlib.h> static void pmask(const int bmsk, const int umsk) { (void) printf("%04o - %04o == %04o\n", bmsk, umsk, bmsk - umsk); (void) printf("%04o & ~%04o == %04o\n", bmsk, umsk, bmsk & ~umsk); } int main(void) { pmask(0777, 0022); pmask(0666, 0022); pmask(0777, 0033); pmask(0666, 0033); return EXIT_SUCCESS; }
実行結果はこんな感じだった。
$ ./a.out 0777 - 0022 == 0755 0777 & ~0022 == 0755 0666 - 0022 == 0644 0666 & ~0022 == 0644 0777 - 0033 == 0744 0777 & ~0033 == 0744 0666 - 0033 == 0633 0666 & ~0033 == 0644 $ _
0666と0033の組み合わせのみ、引き算では0633が、ビットマスクでは0644が得られる。しかし他のパターンでは引き算とビットマスクとで得られる値は同じだ。
手元のUbuntu 16.04で試してみたところ、「umask 0033
」の時に生成されたファイルのパーミッションは「rw-r--r--」、すなわち0644だった、同じLinuxであるArchLinuxと同様に、umask(1)では引き算ではなくビットマスクで計算されているようだ。
ということで、少なくともUbuntuとArchLinuxでは、umask(1)の結果は引き算ではなくビットマスクだ。
問題は、事の発端のドキュメントのOSであるSolarisを含む「Linux以外のUnix系OS」でのumask(1)の挙動を調べていないことと、元文書の「subtract」という単語が引き算以外のニュアンスを持っているか否かが分からないこと、以上の2点だ。実際、どうなのだろう?
Raspberry Pi用にQtをクロスコンパイルする方法を探していたのだが、どの情報も非X11のコンソール環境にてEGLFS経由で直接OpenGL ESで画面描画する構成のQtのバイナリを作るものばかりだった。
しかし私は、諸事情よりX11のデスクトップ上でアプリを実行できる構成のQtのバイナリが欲しいのである。
まあ、Linux上で「Qt for Linux/X11」を自前ビルドするのと同じ方法をRaspbian上で実行すれば、確かにブツは手に入る。だけどPC上でビルドする方が速いので、できればクロスコンパイルしたい。
この文書を書いている時点で、最も有用な文書はQt WikiのRaspberryPi2EGLFSだ。このページの方法でEGLFSを使う構成でQtをクロスコンパイルできる。
以下、上記ドキュメントの補足:
-e ssh
」を追加して、ssh経由で転送する。./configure
」のオプションに「-no-gbm
」を付与する。でないと「make
」した時にビルドに失敗する。/etc/ld.so.conf.d/
」に配置するファイルの名前は、「qt5pi.conf
」ではなく「00-qt5pi.conf
」にする必要がある。ここ4年ほどCore i5-3340Mを搭載したノートPC上でVirtualBoxを使ってきたので、その辺の知見を元に、VirtualBoxなどのx86仮想化ソフトウェアを使ってゲストOSを飼う際に必要な「ホスト環境のハードウェア構成」についてまとめておく。
WindowsをホストOSとして、ホストOS上でWebブラウザや開発環境を動かしつつ、ゲストOSとしてWindows・Ubuntu(GUIとCLIの双方)辺りを動かす――という非Web系のソフトウェア屋さんでありそうな環境を想定している。
――Web屋さんとかインフラ屋さん向けの仮想化の話なんて、割と直ぐに見つかるでしょ(無責任)。
Intel VTやAMD-Vなどの仮想化支援機能をサポートするCPUを選択して、BIOS/UEFI設定で有効化した上で、仮想化ソフトウェア側で支援機能を使用する設定にしないと、ゲストOSの性能は劣化する。
IBMのメインフレームとは異なり、x86のアーキテクチャは元々仮想化なんて考慮されていなかった訳で……x86上で動作するOSも仮想化を考慮しない作りになっている。だから、単純にソフトウェアだけで仮想化を実現しようとするとオーバーヘッドが大きい。
Intel VTやAMD-Vは、このオーバーヘッドを軽減するための後付けの機能だ。
Intel VTについては、VT-xとVT-d双方に確実に対応しているデスクトップ・ノートPC向けCPUは「Broadwell/Skylake以降のインテルCoreプロセッサー・ファミリーに属するCore i7/5/3・Pentium・Celeron」だ。それ以前の世代だと、ローエンドではVT-d非対応であったり、ハイエンドのCore i7/5でもVT-d対応/非対応のモデルが混在していたりする。
Intel AtomベースのPentiumやCeleron*1では、Apollo Lake以降でVT-xとVT-d双方に対応しているようだ。
AMD-Vについては、不勉強なので知らない。
ここでのシングルスレッド性能は(ハイパースレッディングのことを無視するなら)「1コア辺りの性能」を意味する。
シングルコアでの演算性能向上の限界により、PC向けCPUの分野でも2006年ごろからマルチコアによる性能向上に舵が切られた訳だが――それでも依然としてシングルスレッド性能も向上している*2。
正直なところ、仮想化ソフトウェアは重い。何しろ「独立したハードウェア上で直接実行する」という前提で作られているx86向けのOSを無理やりソフトウェア上で動かしている訳で、Intel VTやAMD-Vなどの仮想化支援機能で軽減されるといえども、処理としては重い部類に入る。
ソフトウェアには「シングルスレッド性能が上がると高速化する」ものと「コア数を増やすと高速化する」ものの2系統があるが、経験的に仮想化ソフトウェアでは「1にシングルスレッド性能、2にコア数(ただしデュアルコア以上)」の順に効果があるように思う。
だから、まずはシングルスレッド性能が高めのCPUを選択すべきだろう。
経験的に、またベンチマーク性能的に、シングルスレッド性能は「Intel > AMD*3」という傾向にある。私ならIntelのCPUを選択するだろう。仮にAMDのCPUから選択するのなら、現時点では最低でもRyzen 5の上位モデル以上を選ぶだろう。
IntelのCPUでも、Intel Atomベースのものは例外的にシングルスレッド性能が低いので*4、少なくとも現時点では選択してはならない。
現在使用しているCore i5-3340Mが「ギリギリ実用に耐えうるかな……?」ぐらいの感じなのだが、ベンチマーク的にはKaby Lake世代のデスクトップ向けPentium Gシリーズにて追い抜かれつつある感じだ。他のソフトウェアと同様に、OSもバージョンアップと共に肥大化していく傾向にあるので、余裕を見てKaby Lake以降のデスクトップ向けCore i7/5/3ないしKaby Lake Refreshのモバイル向けCore i7/5に絞り込むのが現時点でのベターだろう。
ある程度シングルスレッド性能が高い場合、次に効果があるはコア数だ。現在使用しているCore i5-3340Mは「2コア、4スレッド」の構成なのだが、できれば「4コア、4スレッド」、欲を言えば「4コア、8スレッド」以上が望ましいと考えている。
最近のPC向けCPUはほぼ全てマルチコアなので、OSもマルチコア環境で性能を発揮するように実装されている。VirtualBoxなどではゲストOSに割り当てるCPUスレッド数を指定できるが、2スレッド以上割り当てることでゲストOSの性能が向上する。
その上で、例えば「4コア、4スレッド」の環境では、ゲストOSに2スレッド(2コア)割り当てたとしても、残り2スレッド(2コア)をホストOSで使用できるので、「ホストOSで作業しながらゲストOSを動かす」という使い方にも耐えられる。
「4コア、8スレッド」の環境なら、「ゲストOSに4スレッド(2コア)、ホストOSに4スレッド(2コア)」とすれば双方ともまずまずの性能となるだろうし、「3つのゲストOSに2スレッド(1コア)ずつ割り当てて、ホストOSに残り2スレッド(1コア)」とすればゲストOSの同時使用にも耐えられる。
IntelのCPUで確実に「4コア、4スレッド」以上なのはデスクトップ向けCore i7/5かKaby Lake Refreshのモバイル向けCore i7/5だ。モバイル向けでは長い間、Core i7/5でも「2コア、4スレッド」の構成が大半だったのだが*5、Kaby Lake Refreshでコア数が増えて「4コア、8スレッド」となった。でもゲストOSを飼うなら、できればデスクトップ向けCore i7/5を使いたい。
ここまでCPUに注目してきたが、ゲストOSを飼うならメモリも欲しい。ゲストOSの同時実行数や、ゲストOSと並行してホストOSで行う作業に依存するが、個人的には16GB以上を推奨する。
ホスト環境に積むべきメモリサイズは「ホストOSで使用するメモリ量+(ゲストOSで使用するメモリ量×ゲストOSの同時実行数)」で決まる、と考えればよい。
例えばWindows 10 Home搭載の格安ノートPCの大半では、メモリサイズは4GBだ。この手のPCは「ネットサーフィン等の軽い作業」向けだ。だから、ゲストOSがWindows 10で且つ軽い作業しか行わないなら、ゲストOSに割り当てる最小メモリサイズは4GBぐらいだと考えればよいだろう。
ゲストOSと並行してホストOS(Windows 10)で行う作業も上記の「軽い作業」ぐらいならば、ホストOS用に確保しておくべき最低限のメモリサイズも4GBとなる。
この時点で、ホストOS・ゲストOS合わせて8GBのメモリを確保すべきだということになる。もちろん「8GB積んでいないと動作しない」という訳ではないのだが、色々と動かし始めた際に動作が遅い等の影響が出る可能性がある。
ソフトウェア開発でありがちな「ホストOSで統合開発環境を動かしつつ、テスト環境としてゲストOSを動かす」というケースでは、ホストOS側で快適に作業するには4GBでは足りないし、テストするソフトウェアが重いのならゲストOSにより多くのメモリを割り当てる必要がある。
メモリサイズはOSの種類によって異なる。例えばUbuntuのSystem Requirementsを見ると、最近のUbuntu Desktop Editionのデフォルト環境ではシステムメモリだけで4GBが要求されている反面、Ubuntu ServerをCLIで運用するならシステムメモリは256MBで済む。この数字は追加のアプリケーション/デーモン(サーバ)を動かさない場合のものなので、実際には上記の値よりも多いメモリを割り当てるべきだろう。
こういったあれこれを考えると、ホストOSがWindowsの場合、「メモリは16GB以上」という要求に当てはまることが多くなる。
CPU・メモリに次ぐ最後の要素は「ディスク」である。まず、ゲストOSを飼うならそこそこ容量の大きなディスクは必須だ。
例えばゲストOSがWindowsの場合、経験的に、Windows 7以降の64bit OSをクリーンインストールしてWindows Updateを当てただけの状態で、可変サイズの仮想ディスクは概ね10〜20GBの大きさとなる。OS以外にアプリケーションをインストールすれば、仮想ディスクはより大きくなるし、スナップショット機能を使えばさらにディスクを消費する。
ゲストOSの数が増えれば、その分だけ仮想ディスクの数が増えるので、もっとディスクを消費することになる。
その上で、さらに経験に基づく話になるが、Windows PCではディスクの空き容量が少なくなりすぎると動作が遅くなることが多い。パーティションの切り方、OSのバージョン、常駐しているウイルス対策ソフトの種類その他に左右される話なので、未だに具体的な空き容量の目安は分からないのだが……しかし、空き容量が少なすぎると動作が遅くなることが多い、という傾向が見られる。
だから、ゲストOSの仮想ディスクを抱えつつもある程度の空き容量を確保できるだけのディスクサイズが欲しい。そうなると、用途次第ではあるが、そこそこ容量の大きなディスクが必要となる。
もう1つ重要なのが、ディスクIOの速度。バス速度も含めたIOの話だ。
ゲストOSがディスクを読み書きすると、仮想ディスクの読み書きが発生する。つまり、仮想ディスクを置いている「ホストOSのディスク」へのIOが発生する。ゲストOSが大量にディスクを読み書きすると、ホストOS側で他の作業に影響が出てしまうことがある。
この問題を解決するには、高速なディスクを使用するなどして、ディスクIOを高速化するしかない。
さらに言えば、HDDではなくSSDを採用して、仮想ディスクもSSDに置くようにすれば、より快適になるはずなのだが……1TB以上のSSDはまだまだ高いのである。残念。
*1:Pentinu JやCeleron Nなどの、JやNで始まるもの。
*2:もちろん上昇率は以前よりも低めだが、それでもじりじりと上昇している。
*3:ただし同価格帯での比較。
*4:ベンチマーク的には、最近の格安ノートPCやWindows Storage Server搭載NASに搭載されることの多いCeleron J1900やCeleron N3160は、Pentium M(2000年代前半のノートPCに搭載されていたCPU)よりもシングルスレッド性能が低い。割と新しいPentium J4205にて、ようやくシングルスレッド性能がCore 2 Duo世代のモバイル向けモデルに追いついてきた感じだ。
*5:Core i7のPerformanceモデルなら「4コア、8スレッド」だったが、該当するCPUを載せたノートPCが軒並み高かったり、ゲーミングPCでの採用が多かったり、17インチ大画面で持ち運びに不便だったりと、なかなか良い機種に巡り合えなかった。
2018-06-30現在のステータス。id:eel3:20170708:1499445416 から1年経て、こうなっている。
なおCSS、HTML、XMLは除外する。人工言語ではあるけれども「プログラミング言語」という括りからは外れると思う。
*1:とはいえ、C++標準ライブラリの充実度をJavaやC#(.NET Framework含む)のそれと比較してはいけない。
*2:でもKotlinやSwiftを触ると、C++の型推論(というか型チェック関連)は「もう少し頑張ってほしいなあ」という印象を持ってしまうから不思議だ。アレでも以前より随分と便利になったんだけどなあ。
*3:これでもsedはチューリング完全な言語だと証明されているらしい。
*4:少なくともC++の言語仕様は私の手には余る。自分が把握している範囲の機能でコードを書くのは好きなのだけど。
*6:私の認識では、JavaScriptは、第一級関数やクロージャやお仕事用のメジャーな言語に組み込まれて、少なくない人が使う契機となった言語だ。
*7:――といってもシミュレータだけど。
*8:支障がある部分を触るほど深入りするには、あと20年ぐらい掛かるのではないか?
*11:ClojureやScalaに関しては、同様の機能を私が見逃している可能性も高いのだが。
そろそろ言及しておこうかな。これって「コメントのいらないプログラムの書き方」ではなくて「コメントを減らす技法 初級」だよね、それも割と暗黙の前提が多めの。
以前 id:eel3:20160425:1461594142 で言及したのだけど、ソースファイル中のコメントを減らすことは、ある水準までは技術の問題だ。しかし一定水準を超えると技術ではなく(そのプログラマが所属する)環境・文化の問題となる。
コメントの無いプログラムは、「コメントを減らす」の究極形だ。なので環境や文化の問題は避けられない。
であるにもかかわらず、件の記事は技術の問題(それも割と初級のもの)しか扱っていない上に、特定の文化圏を前提とする内容を、当該文化圏について明示せずに書いてしまっている。だからはてぶのコメント欄が割と否定的な内容になっているように思う。*1
先の記事では「『コメントのいらないプログラムの書き方』のポイント3つ!」として、次の3点を挙げているのだけど:
(1)は半分は技術の問題で、もう半分は特定の文化圏を前提とする内容だ。(2)と(3)は完全に特定の文化圏を前提とする内容だと言える。
ところで先ほどから何度も「文化圏」と書いているけど、この記事を書いた人の文化圏は以下の別記事から推察できる。
上記の記事を読んで、若干の想像を膨らませてから、件の記事を読めば「まあ、そういう結論になるよね」と納得できる。もっとも、例えば私の所属する文化圏は随分と異なるので、件の記事を読んだところで「でも、ウチだとそうはいかないよね」という感想しか出てこない。コメントは減らせるけど、コメントがいらなくなることはないのよ、ウチの文化圏では。
先にも書いたが、コメントを減らすことは、ある水準までは技術の問題だ。
DRY(Don't Repeat Yourself)の視点で考えると、コメントを書くことでソースファイル中の特定のソースコードとの間に「記述の二重化」が発生するのならば、そのコメントを削除できない検討すべきだ。
だから名前(ファイル名、クラス等のモジュールの名前、関数などのサブルーチンの名前、定数や変数の名前)には適切なものを付けるべきだし、直値ではなく名前付き定数を使うべきだし、モジュールやサブルーチンのインタフェースには極力自然なスタイルを採用すべきだ。上手くいけば、下手な名前や変なインタフェースを誤魔化すための補足コメントが不要となるので、結果としてコメントを減らすことができる。
また、ソースファイルはas is、つまり「現在の姿」を映し出すものでなくてはならない。
だから「過去の姿」である変更履歴の類は「バージョン管理システムのコミットログ」や「プロジェクト管理システムのチケットのコメント」に書くべきで、ソースファイル中のコメントに書くべきではない。古いコードをコメントアウトして残しておくなどもってのほかだ。
DRYとas isの両者の観点では、プログラムに変更が発生する可能性を考慮すると、変更が発生しやすい要素についてはコメントを書かずに済むか検討すべきだろう。なぜならば、その要素にコメントが存在する(つまり「記述の二重化」が発生している)と、プログラムの変更時にas isとなるように修正すべき部分が2倍となる可能性が生じるからだ。
だから、変更が発生しやすい「式とステートメント」に密着したコメントを極力書かずに済むように「適切な名前・適切な順序・明解なインタフェース・シンプルなアルゴリズムの採用」という点を心掛けるべきだ。式やステートメントそのものの可読性を高めれば、補足のためのコメントは少なく済む。実際のところ、世の中の「コメントを書かない」系の指摘の大半は「式とステートメント」に密着したコメントをターゲットとしている。
しかし一方で、「式とステートメント」よりは変更が発生する可能性が低い要素である「データとデータ構造」については、『プログラム書法 第2版』に曰く「データの割り付けかたについての解説をつけよう」である。
プログラムに解説をつけるためにも、もっとも効果的な方法の一つは、単にデータの割り付けかたをくわしく説明する、というものである。おもな変数について、その値としてはどんなものが可能かを示し、それが変って行くようすを説明すれば、それだけでプログラムの解説は、ずいぶん進んだといってよい。
もちろん、これを『プログラム書法』の時代と同じくコメントをして記述するのか、それとも21世紀らしく別ドキュメントに記述してリンクさせるか、という点は(そのプロジェクトの実情を考慮しつつ)議論すべきだろう。
――そう、「(そのプロジェクトの実情を考慮しつつ)議論すべき」という辺りから、技術の問題よりも環境・文化の問題の割合が増えてくる。
まだ私の中で決定版の答えは出ていないのだが、「コメントの少ないプログラム」から「コメントのいらないプログラム」の間には、少なくとの以下の環境・文化的障壁が存在するのではないかと考えている。
まず「使用する言語の抽象度」。解くべき問題に合致している抽象度の言語を用いているなら、コメントは削りやすいものだ。例えば次のスクリプトは、中身が短く簡潔で分かりやすいので、コメントは不要かもしれない。
#!/usr/bin/awk -f # 一列目の合計を付け足す。 { n += $1 print $0 } END { print n }
しかし、これがC言語で(しかも全て自前で)実装されていたらどうだろうか? 少なくとも「このプログラムは何か? 想定している入力・出力は何か?」という情報は必要だろう。
世の中、常にモダンで抽象度の高い言語が使えるとは限らない。私なんて組み込み系の人なので、C言語縛りとか「言語仕様だけC++11(標準ライブラリはC言語と同じ)」縛りとか、そういうことが結構ある。
次に「開発環境」。「使用する言語の抽象度」で挙げた例を掘り下げて考えてみよう。
適切なドキュメントが存在し、継続的にメンテナンスされていて、かつソースファイルから関連ドキュメントへの参照(またはその逆)が容易である環境であるならば、「このプログラムは何か? 想定している入力・出力は何か?」という情報をコメントに書く必要はない。どちらかと言えば、ドキュメント化しておくべき情報だろう。
しかし、そのような開発環境が整っていないのならば、ソースファイル先頭のヘッダコメント辺りに記述しておく意味は十分にあると言える。
「そのソースファイルの性質」は、例えば「自社開発アプリケーションのコンポーネントの一部で、概ね固定されたメンバーで管理しているソースファイル」なのか、「オープンソースで公開しているライブラリで、開発チームがメンテしていて、外部の複数のプロジェクトにて組み込まれるソースファイル」なのか、というような性質の違いのことだ。
メンテナンスする人が限られていて、利用者も固定されがちで、外部への露出が少ないならば、ライブラリの公開インタフェースの情報は簡素で済む。場合によっては、インタフェース仕様のドキュメントを書かずにテストコードで示すだけでも問題ないだろう。
一方で、不特定多数の人に公開されるライブラリのソースファイルでは、問い合わせを減らすためにドキュメントを充実させる傾向にある。大抵はAPIリファレンスの類が存在するはずだ。このような環境では、ソースファイル中のコメントとしてAPI仕様を記述した上でツールでドキュメントを生成する、という手法が取られることも多い。
少なくとも私は、Unixのシステムコールを叩くときも、AppleのiOS SDKを使う時も、APIリファレンスとサンプルコードの双方を見る。全く知らない第三者が使うかもしれないライブラリにおいては、使い方を示すのにテストコードだけでは不十分で、どうしてもAPIリファレンスが欲しくなる。この時、横着してソースファイルにAPI仕様を記述してしまうことは結構あるものだ(もちろんそこからドキュメントを生成させる訳で、別に「コメントを読め!」という主張ではないのだが)。
「作成者と利用者の間の『暗黙知』の問題」は、ソースファイルの性質とも絡んでくる話だ。
開発メンバーがある程度固定された環境では、明示的・暗黙的にかかわらず、例えば「○○の場合は××のスタイルで書け!」のようなお作法があったりする。この環境に適応すると、ソースコード中の「××のスタイル」を目にした時に「ああ、ここは○○なのだな」と容易に逆引きできるようになる。
名前の付け方やインタフェース定義の流儀など、明快かつ合理的な作法(型)が存在し、皆が作法に従っているなら、コメントを読むまでもなく暗黙のうちに理解できてしまう部分が生じるものだ。
だから「自社開発アプリケーションのコンポーネントの一部で、概ね固定されたメンバーで管理しているソースファイル」の場合、作成者も利用者も同質性が高い上に、同質性が高い人同士での利用に留まるので、暗黙知にもとづく部分の補足コメントを削ってもあまり問題にはならない。むしろ冗長性が排されるので歓迎されるかもしれない。なぜなら、彼らからすれば「当たり前の常識」で、わざわざコメントに書くほどのことではないからだ。
しかし作成者と利用者を含むメンバーの同質性が低い場合は、安易に暗黙知に頼ってコメントを削ることは危険だ。メンバーが固定されがちな傾向にあるならば、開発者向けドキュメントに暗黙知をまとめた「お作法」の項を追加して守らせる、という方法(つまり新規参加者を教育して同質性を高める方法)は有効だ。しかしそうもいかない環境では、冗長になることを諦めて、毎度毎度コメントで暗黙知の部分を補足せざるを得なくなる。
私の場合、メンバーの同質性が高いとは言えない環境での開発が多い。なので、非公開ルーチンの類であっても、名前・引数・戻り値を十二分に練り上げた上で、ダメ押しとしてルーチンの仕様をコメントで記述した上で、コメントで記述した仕様に従ってユニットテストを書いている(それが可能な程度には詳細に記述している)。数年後に他の人がメンテナになった際に、非公開ルーチンのAPIリファレンス代わりにコメントを読んでもらうことを期待しているのだ。もちろん、その程度のコストをかけることが許される環境である、という前提があるのだが。
ソースレビューによる同質性の担保も、やはりメンバーの同質性が低い環境では機能しにくい。以前、異なるバックグラウンドを持つ開発者3人をかき集めて短期集中2ヶ月でモノを仕上げる(で、終わったら即解散する)プロジェクトに関わったことがあるのだが、バックグラウンドが違い過ぎて、コーディングスタイルの統一すら無理だった(そんな時間すらなかったこともあるけど)。なので、プロジェクトの成果物は3種類のスタイルで記述されている。同一ファイルに複数のスタイルが混在していない点が唯一の救いだろう(ファイル毎には結構異なる)。このような環境では、レビューで同質性を担保することなど望めない。
しかし継続的なプロジェクトでメンバーが固定されがちなら、ソースレビューで同質性を担保することが可能だ。そして同質性が担保されるなら、暗黙知の部分を削る余地が出てくるだろう。
適切な技法を採用することでコメントの量を減らすことが可能だが、ある水準からは環境・文化の問題となる。だから、ソースファイルからコメントを全て取り除くためには、環境・文化の問題と向き合わなくてはならない。
「コメントの少ないプログラム」から「コメントのいらないプログラム」の間には、少なくとも以下の環境・文化的障壁が存在すると考えられる。
そして、環境・文化の問題は、必ずしもクリアできる代物とは限らない。
「コメントが無くても読めるようなプログラム」は「コメントの無いソースファイル」を意味しない。「ソースファイルにコメントを書かずに済むように、ありとあらゆる合法的かつ妥当な手法を駆使した結果、そのコードを共有する文化圏の基準において最小限のコメントのみが、メンテナンスしやすいスタイルで記述されているソースファイル」のことだ。
「そのコードを共有する文化圏の基準において最小限のコメント」が「少ないコメント」と「コメントなし」のどちらを意味するかは、「文化圏の基準」に依存する。だから環境・文化の違いが存在することを念頭に置いてすり合わせしないと、いつまで経っても議論は平行線のままだ。
*1:それこそ記事のタイトルが「コメントを減らす技法 初級」とかだったら、まだ荒れなかっただろう。その代わり、読まずにスルーする人は多かっただろうね。
世の企業も求むる「コミュニケーション能力を持つ社員」といふものを、エンヂニアリングの現場でも求めてみむとてするなり。
「コミュニケーション能力」という言葉は実に曖昧で、10人いれば5〜6パターンぐらいは異なる解釈が出てきそうな代物だ。この文章では、ソフト屋さんを含む「エンジニアリングの現場にいる技術者」に求められるコミュニケーション能力の実体について、基本的なところを述べてみたい。
(ただし私はプログラマなので、ソフト屋寄りの見解となる)
コミュニケーション能力を「社会生活を営む人間の間で知覚・感情・思考を伝達するための能力」であると仮定した上で、掘り下げてみよう。
コミュニケーション能力を獲得するための第一歩は、経験の言語化だ。一般に、人間はことばという共通のフレームを用いて情報を伝達する。経験を言語化できなければ、経験を相手に伝えることができないし、また相手の言葉を理解する(ことばを経験にマッピングする)こともできない。
一般的に、経験の言語化は幼少期に家族などの特定の親しい人との会話によって形成され始めるらしい。ただ、発達障害(とくに自閉症)の場合、会話によってこの辺の能力を形成していくのが難しい傾向にあるようだ(ではどうすればよいか、という点については色々と研究されている模様)。
経験を言語化できていると仮定すると、次の論点は、発達心理学でいう一時的ことばと二次的ことばだろう。
一時的ことばは、同じコンテキストを持つ親しい相手と1対1でコミュニケーションする際に用いることばだ。経験の言語化が、幼少期に家族などの特定の親しい人との会話によって培われるために、一時的ことばは「お互いについての知識・経験を共有している」という前提のものとなる。だから、不完全な表現であっても相手は共有する知識・経験から補完して理解することができる。
二次的ことばは、未知の人も含む不特定多数の人とコミュニケーションする際に用いることばだ。この場合、「お互いについての知識・経験を共有している」という前提は成立しない。そのため、情報を発信する側は、発信する情報を理解するために必要な情報を全て言語化し、整理した上で、表現する必要がある。同時に、情報を受信する側は、内容を理解するために必要な情報を全てことばの文脈から抽出し、個々の情報の関連付けを行うことで、理解する必要がある。
二次的ことばは、スピーチのように不特定多数にたいして語る場面だけでなく、チームミーティングでの進捗報告や、業務手順書の作成のように、「ある程度共通するバックグラウンドを持つが、『家族などの特定の親しい人』ほどはお互いについての知識・経験を共有していない、特定少数の人々」との情報伝達でも用いられる。お互いに共有している知識・経験が限られているため、情報を発信する際には、相手が内容を正しく理解するに足るだけの情報を提供しなくてはならない。
社会人に求められるコミュニケーション能力は、「二次的ことばによる情報の伝達がスムーズに行える」ということである。つまり、自分が伝えたい内容について相手側の知識・経験が乏しい場合でも正しい情報を伝達できることであり、その逆に相手が発信する場合にも会話・文章ということばだけから正しい情報を理解できることである。
要するに「話が面白い」とか「会話が弾む」などの面で評価が高い人であっても、それが「一時的ことばによる身内間での情報伝達」に留まるのならば、それは「社会人に求められるコミュニケーション能力」を兼ね備えていることを意味しない。
さて、二次的ことばによるコミュニケーションでは、情報を全てことば(相手の話した内容や、文章に書かれている内容)だけから理解していく必要がある。これを実現するには、ことばに含まれる単語を正確に理解し、単語を連結している文法を正確に理解する必要がある。また自分自身が発信側となる場合には、単語を正確に用いつつ、単語を連結する文法を正確に用いなくてはならない。
社会人(というか職場)でのコミュニケーションで用いられる単語には、日常でも用いられる単語だけではなく、その環境に応じた専門用語・業界用語の類も含まれる。エンジニアのコミュニケーションにおいては専門用語が多用される傾向にあるので、各専門用語の意味を正確に理解して用いる必要がある。
なぜ専門用語が多用されるのか? お互いに「相手はその専門用語を知らない」という前提でコミュニケーションしようとすると、内容が冗長になりすぎて、全てを伝えるのに時間がかかりすぎてしまうからだ。だから、専門用語として厳密に意味を定義した上で、双方とも定義された意味を理解しているという前提で情報伝達を行うのである。
例えるなら……バリカタを知らない人にバリカタという言葉を使わずに「あのラーメン屋のバリカタは柔らかすぎでバリカタじゃない」ということを伝えようとするようなものである。バリカタのラーメンを食べたことがない人は、バリカタを「通常よりもかなり硬い麺」に置き換えて説明されたとしても、そもそもバリカタの硬さを理解していないので、「あのラーメン屋のバリカタという名の『バリカタよりも柔らかい麺』」がどの程度の硬さなのか類推できない。だから、単に「通常よりもかなり硬い麺」に置き換えるのでは駄目で、相手が食感を覚えている食べ物のうち、「バリカタに近い硬さのもの」と「あのラーメン屋のバリカタという名の『バリカタよりも柔らかい麺』に近い硬さのもの」を探り出して提示しなくてはならない。しかし身をもってバリカタを知っている人同士ならば、バリカタで十分通じるものだ。だから時には「話をする前に、今から『本当にバリカタのラーメン』と『バリカタという名のバリカタよりも柔らかい麺のラーメン』を食べ比べに行こうぜ!」という方法も有効だ。ことばで説明するよりは早く済むだろう。でも食べ比べの時間は余分にかかるから、結局は、バリカタを知っている人同士の方が情報伝達が早く済むことになる。
エンジニアが用いる専門用語は、その職場・チームといった環境に特異のものだけでなく、他の環境のエンジニアを含むもう少し広い範囲(例えば業界)にて共通言語として用いられる専門用語もある。例えばソフトウェア・エンジニアの場合、基本情報技術者試験に出てくるような用語は、専門用語の中でも広い範囲で用いられるものだ(スレッドという単語が通じるソフト屋さんは少なくないはず)。そういった公知の専門用語をどれくらい正しく理解して使用できているか――という点もエンジニアのコミュニケーション能力に含まれてくる。
ただし専門用語の知識の有無は人によって異なるので、情報を発信する側は、文脈によって専門用語を用いるか否かを切り替える必要がある。例えば、新入社員と会話する時と同期の社員と会話する時では、前者よりも後者の方が専門用語の割合が多いはずだ。
また、エンジニアのコミュニケーションでは、抽象度の高い事象や、大きくて一度に理解できない事象を取り扱うことが多い。そのため、同じ情報を後から何度でも参照できるように、会話だけでなく「文章を用いた二次的ことばによるコミュニケーション」が多用される傾向にある。文章によるコミュニケーションでは、会話でのコミュニケーションでは可能な「その場のニュアンスで不完全な表現を補完する」ということが不可能なので、単語の内容や、単語を連結する文法を、会話の場合よりも正確に用いなくてはならない。
その上で、文章だけでは抽象度の高い事象を正確に伝達できない可能性があるため、時には適切な図表を用いる必要もある。事象によっては数式で表現することもあるだろう。ソフト屋の場合は、時にソースコードという人工言語にて他のエンジニアとコミュニケーションを図ることもある。
つまるところ、文章と図表を用いて情報を正確に発信できること、文章と図表から情報を正確に読み取ることができること、この2点である。この辺りは、大学教育でのレポートや論文の作成にて発信側を、レポート・論文作成の際の文献調査にて読み取り側を研鑽するように思う(で、その前提となる「普通の文章の読み書き能力」は義務教育で学ぶ……と書いてしまって大丈夫だろうか?)。
まとめると、エンジニアに求められる「コミュニケーション能力」とは、以下を兼ね備えていることである。
基本的に、エンジニア同士のコミュニケーションでは「正確な情報をスムーズに」という点が重視される。エンジニアが取り扱う事象の大半は、抽象度の高いものや、大きくて一度に理解できないものなどの、複雑な事象だ。複雑なものを取り扱うには正確な情報が必要であるし、複雑なものを取り扱うことに集中するためにも余分なコスト(「情報が不正確かもしれないので再検証する」という時間的ムダなど)を払いたくないものだ。
特に、簡単な事象が過去に解決されてきた積み重ねの結果、エンジニアリングの現場にて取り扱う事象が高度化している昨今では、複雑な事象にチームで取り組むことが多い。『人月の神話【新装版】』のブルックスの法則にて言及されているように、チーム内で相互コミュニケーションが求められる場合、コミュニケーションの労力は人数nの時「n(n - 1) / 2」に比例する。このような本質的問題を抱える状況において、「情報が不正確である」とか「情報は正確だが、読み解くのに苦労する」などの偶有的問題まで発生したら、進むものも進まない。それゆえに、エンジニアは「正確な情報をスムーズに」という点を重視する傾向にある。
だから、世間一般で言われるコミュ障(雑談が苦手・苦痛なタイプ)でも、業務上必要な情報を正確かつスムーズに伝えられるのなら、大抵は問題ない。もっとも、コミュ障ゆえに対人関係の構築に不慣れな場合、業務上必要な情報を伝達する際に相手が把握している情報量をうまく推測できないために、「どこまで情報を付与すれば相手が理解できるか/どこまで省略しても相手が理解できるか」を見誤ることはある*1。
まあ、最初から上記3項目を全て高いレベルで身に着けている新卒社員は少ないだろうから新人教育やOJTで最低限鍛えるぞ――というのがジャパニーズ・トラディショナル・カンパニーの伝統行事なのだが、しかし会社にも「許容できる教育コスト」に上限がある。だから、新卒採用にて「コミュニケーション能力のある人=上記(1)と(2)がある程度の水準に達している人」を求めるのだ、その辺の能力を正しく計測できて採用できているか否かは別として*2。
――いや、自己分析するに、「オブジェクト指向」ではなく「特定のオブジェクト指向プログラミング言語」がしっくりこないことがあるんだな、これが。
具体的には、触ったことのある言語ではJavaとC#だ。この2つの「しっくりこなさ」具合からすれば、C++の方がマシだ(もっとも、C++は別の部分で好きになれない面があるのだが*1)。
C#は経験が浅すぎるので、Javaについて。Javaは、ここ数年の間にちょっとしたコンソールアプリの実装とAndroidアプリの開発で使用した。興味深いことに、コンソールアプリを書いた際は「しっくりこなさ」全開だったのだが、Androidアプリを書いた時にはその手の違和感はなかった。
この違いは何だろうと考えて、ふと気づいた。おそらく私は、クラスの定義を強要されることに違和感を感じる体質なのだ。
私のプログラマとしての土台(第一プログラミング言語)はC言語だ。C言語にはクラスはない。次に使うことが多いC++は、クラスを定義するも定義しないも活殺自在な言語だ。JavaScript/JScriptやVBScriptも、クラスを定義する/しないは比較的自由だ。小ツール実装で時々使うRubyとPythonは、ちょっとしたスクリプトであれば構文の見た目として「クラス定義なんて知らないよっメソッド定義だけだよ!」風に記述できる。
これらの言語のうち、C言語とC++のアプリケーションメインエントリは関数だ。JavaScript/JScript・VBScript・Ruby・Pythonでは、必須構文としてのアプリケーションメインエントリ的なものはない*2。
つまり、C言語を除けば、コードの見た目として「必要になったらクラスを定義するが、必要なければ使わない(関数/メソッドを単独で定義して用いる)」というスタイルで記述することが可能な言語に慣れている(そしてC言語にはそもそもクラスが存在しない)。
しかしJavaやC#は、例えばC++にてmain関数と2つのサブ関数で記述できるような小ツールであっても、クラスを定義した上でmainメソッドを記述しなくてはならない。この時点で、何というか「この程度のことでクラスを持ち出すなんて……」と思ってしまうのだ。
その一方で、Androidアプリのような複雑な*3ソフトウェア、特にGUIアプリケーションというオブジェクト指向プログラミング的アプローチに向いているソフトウェアでは、クラスを用いる意味もメリットもあるので、違和感を感じることがない。とはいえ、時としてクラスに属しないメソッドを書きたくなることはある。C++ならば名前空間に直接属する関数として、Objective-CならC言語流のファイルによるモジュール分割を用いたデータ結合の関数として定義するだろう、ちょっとした汎用のユーティリティ・メソッドだ。
別の視点から考察すると、どうも私は「構造体(=レコード型)」の影響を強く受けていて、「何らかの関連がある複数のデータ+それらを操作する専用のルーチン=オブジェクト」という意識が強い。C言語で構造体を使用してややオブジェクト指向プログラミングっぽいアプローチをすることもあるが、その場合、「何らかの関連がある複数のデータ」を一まとめにする必要があるならば構造体にまとめるが、必要なければまとめない。
つまり、心理的に「クラス≒抽象データ型≒レコード(複数のフィールド)と操作用ルーチン」という意識がある。抽象データ型として扱うのが妥当ならばクラスにするが、それ以外の用途――例えばJavaやJavaScriptのMathクラスのような使い方――でのクラスの採用に消極的というか、クラス以外の代替機能を使おうとする個人的傾向がある。
仮にMathクラスのようなものをC++で再実装するならば*4、個人的にはクラスではなく名前空間を使用して、関数や定数をひとまとめにするだろう。
このようなアプローチは、例えばC++で採用するには問題ない。しかし、慣れ故にJavaやC#でも採用しようとしてしまい、メソッドをクラス内でしか定義できないことに気づき、ついついもやもやしたものを感じてしまう。
現実には、プログラミング言語には「言語仕様」という制約がある訳で、その制約の元では、例えば「『C++の名前空間』のような機能がないので、代わりにクラスを使う」などのアプローチは合法的だろう。だから、もやもやしたものを感じてしまう必要など全くないのだが……。
結局のところ、慣れの問題なのだ。クラスのない世界での生活が長く、また必要に応じてクラスを定義するか否かを切り替えられる言語に慣れてしまっていて、クラスが必須な言語には慣れていないので、違和感を感じるのだろう。
蛇足:クラスが必須な言語に違和感を感じる一方で、CやC++のような関数が必須な言語(ステートメントを関数の中にしか記述できない言語)には違和感を感じないあたり、どう考えても慣れの問題以外の何者でもない。