書籍購入:『Kotlinイン・アクション』

軽く中身を見た。

Kotlinイン・アクション

Kotlinイン・アクション

本書は、Javaでの開発経験がある人が、『Kotlin入門までの助走読本』やKotlinの公式リファレンスなどで基本を学びつつ、同時に本書を参照して言語としてのKotlinの理解を深めていく――という使い方に向いているように思う。

裏を返せば、本書を読み進めるには「一定以上のJavaの経験」が必要であるし、「Kotlinの基礎文法」の学習に関しては他の文書を併用した方が良い。

それなりに人を選ぶ本だ。

「組込み開発=C言語」というイメージはどこまで正しいか?

組込み開発でのプログラミング言語というとC言語(時にアセンブラ)が真っ先に挙げられる気がするのだが、実務的にどこまで正しいのか書いておこうと思う。

結論から言うと、組込みプログラマの立場としては「組込みプログラミング=C言語」は概ね正しいのだが、組込み業界という視点では「C言語以外も結構使われている」ということになる。

そもそも組込み業界ではどんなソフトウェアが作られているだろうか?

組込み業界と言っても結構広いので、分野によって色々と異なるものだが、私が関わっているのは「ガジェット・スマート家電」寄りの分野だ。作っているソフトウェアは、組込みシステム向け三層アーキテクチャ(ドライバ・ミドルウェア・アプリケーション)が適用される程度には大きい*1。OSはRTOSの類が大半だ。

で、外注も含めて、書かれているソフトウェアは以下のような感じだ。

  1. ハードウェア制御用のドライバ・ファームウェア
  2. 移植性のあるミドルウェア
  3. 組込み機器のアプリケーション層
  4. 組込み機器をパソコンと接続するためのWindowsデバイスドライバ
  5. 組込み機器と連携するWindowsアプリケーション
  6. 組込み機器と連携するmacOSアプリケーション
  7. 組込み機器と連携するAndroidアプリケーション
  8. 組込み機器と連携するiOSアプリケーション
  9. 組込み機器と連携するWebサービスのクライアントサイド
  10. 組込み機器と連携するWebサービスのサーバサイド

今の時代、リッチな組込み機器は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言語が使えなくても組込み業界に入ることは可能だ。結構、機器と連携するモバイルアプリ開発とかで、AndroidiOS向けアプリ開発の手練れを探している企業は多いと思う。

ただし、少し特殊な要件がある場合は、例えばモバイルアプリ開発でもC言語C++の知識が求められることがある。リアルタイム性に関する性能要件がやや厳しいので核となる部分をCやC++で実装する必要があるとか、機器とのTCP通信にて独自のプロトコルを使う必要があってクライアント側としてCやC++で実装されたモジュールが提供されるので組み込む必要があるとか、そんな感じ。なのでAndroidならNDKの、iOSならC++11のスマートポインタとObjective-C++の知見*2が要求されることになる。

(だから先の一覧のmacOSiOSAndroidアプリの開発言語にC++を含めている)

結論として、繰り返しになるが、組込みプログラマ的には「組込みプログラミング=C言語」という構図は概ね正しい。しかし組込み業界という俯瞰した視点では「C言語以外も結構使われている」ということになる。業界にはデバイス制御プログラム以外のソフトウェアを書いている人もそれなりにいるのだ。

なお、上記の議論は「製品としてリリースするソフトウェア」を前提としている。開発用の小ツール――内製ツールやプログラマの個人用ツールとしては、テキスト処理に長けたスクリプト言語Excel制御用の言語(VBAPowerShell)がごく普通に採用されているものだ。ただ、全般的に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の学位が無くても拒否されないだろう)。

あとアメリカの企業の場合、日本の企業よりも「職務不適格による解雇」は行われやすいだろう。

だから、スタートラインに着くことができたジュニアも、ベテランやシニアにステップアップできなければ、少なくとも職業プログラマとしては淘汰されるだろう(もちろん、キャリア開発を経て別分野に転身するなら話は別だが)。

一方で、元記事にもあるように、企業側はジュニアを受け入れてキャリア開発を支援する体制を整える方が健全だろう。誰だって最初は(職業プログラマとしては)素人なのだ。

ところで、ジャパニーズ・トラディショナルな新卒一括採用では、企業によっては「計算機科学とは無縁で、プログラムを書いたこともない人」みたいなノービスがソフトウェア開発者として採用されることがある。採用時に所属学部などでフィルタしている場合も、採用の過程でプログラムを書かせるところは稀だろう。

つまり日本企業では、ソフトウェア開発者としてジュニアに満たないノービスが採用される可能性が(少なくともアメリカのテック企業よりも)高い傾向にある。

あと、「職務不適格による解雇」に値する水準が高く設定されている(でないと解雇の妥当性をめぐる訴訟リスクを抱える)点もアメリカとは異なる。

そういった日本独自の事情を勘案した上で、幾分か割り引いて件の記事を読むべきだろう。

個人的には、ジュニアを採用した場合でも彼/彼女がベテランに育つか否か博打な面があるのに、ジュニアよりも教育コストがかかる――そして心が折れて数年で退職してしまい、それまでかけたコストが水泡に帰する可能性がある――ノービスを採用するのはリスクが大きいと思う。

せめて情報系の学部卒や専門卒に絞り込むとか、もしくは「職業訓練で半年座学と実習を受けながら基本情報技術者を取りました」ぐらいに気合いの入った退路の無い人でないと……教育するにしても、ジュニアとノービスではスタート位置が大きく異なるからなあ。

*1:フィギュアスケートの競技会の年齢別クラスはシニア、ジュニア、ノービスらしいので、ジュニアよりの下のクラスを意味する言葉としてノービスを使っている。

umask(1)は引き算しない、多分。

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をクロスコンパイルする――ただしQtはX11上で動かしたい

Raspberry Pi用にQtをクロスコンパイルする方法を探していたのだが、どの情報も非X11のコンソール環境にてEGLFS経由で直接OpenGL ESで画面描画する構成のQtのバイナリを作るものばかりだった。

しかし私は、諸事情よりX11のデスクトップ上でアプリを実行できる構成のQtのバイナリが欲しいのである。

まあ、Linux上で「Qt for Linux/X11」を自前ビルドするのと同じ方法をRaspbian上で実行すれば、確かにブツは手に入る。だけどPC上でビルドする方が速いので、できればクロスコンパイルしたい。

Raspberry Pi向けのQtクロスコンパイル(EGLFSを使う版)

この文書を書いている時点で、最も有用な文書はQt WikiRaspberryPi2EGLFSだ。このページの方法でEGLFSを使う構成でQtをクロスコンパイルできる。

以下、上記ドキュメントの補足:

  • 上記ドキュメントでは、Raspberry Piの開発元でメンテしているクロスコンパイラを使用している。Raspberry Piの公式ドキュメントKernel buildingによると、中の人はUbuntuでこのクロスコンパイラを使うことが多いらしいので、実績のあるUbuntu上でクロスコンパイルした方が無難かもしれない。
  • 事前にRaspbian側でssh接続を有効化しておく。
  • rsync(1)でファイル転送する際は、上記ドキュメントのrsync(1)の実行例のオプションに「-e ssh」を追加して、ssh経由で転送する。
  • Qt 5.11系をクロスコンパイルする際は、「./configure」のオプションに「-no-gbm」を付与する。でないと「make」した時にビルドに失敗する。
  • /etc/ld.so.conf.d/」に配置するファイルの名前は、「qt5pi.conf」ではなく「00-qt5pi.conf」にする必要がある。

X11用にクロスコンパイルするには?

色々調べた結果、先に挙げたドキュメントの「./configure」のオプションを以下のように調整することで、RaspbianのX11上でアプリを実行する構成でクロスコンパイルできることが分かった。

  1. オプション「-opengl es2」を「-opengl desktop」に変更する。
  2. オプション「-no-eglfs -qpa xcb」を追加する。
  3. Qt 5.11系をクロスコンパイルする場合はオプション「-no-gbm」を追加する。

他の作業については、特に変更点はない模様。

クライアントWindows上で仮想化ソフトウェアでゲストOSを飼うためのハードウェア構成

ここ4年ほどCore i5-3340Mを搭載したノートPC上でVirtualBoxを使ってきたので、その辺の知見を元に、VirtualBoxなどのx86仮想化ソフトウェアを使ってゲストOSを飼う際に必要な「ホスト環境のハードウェア構成」についてまとめておく。

WindowsをホストOSとして、ホストOS上でWebブラウザや開発環境を動かしつつ、ゲストOSとしてWindowsUbuntuGUICLIの双方)辺りを動かす――という非Web系のソフトウェア屋さんでありそうな環境を想定している。

――Web屋さんとかインフラ屋さん向けの仮想化の話なんて、割と直ぐに見つかるでしょ(無責任)。

仮想化支援機能をサポートするCPUを選択する。

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・PentiumCeleron」だ。それ以前の世代だと、ローエンドではVT-d非対応であったり、ハイエンドのCore i7/5でもVT-d対応/非対応のモデルが混在していたりする。

Intel AtomベースのPentiumCeleron*1では、Apollo Lake以降でVT-xとVT-d双方に対応しているようだ。

AMD-Vについては、不勉強なので知らない。

シングルスレッド性能がそこそこ高めのCPUを選択する。

ここでのシングルスレッド性能は(ハイパースレッディングのことを無視するなら)「1コア辺りの性能」を意味する。

シングルコアでの演算性能向上の限界により、PC向けCPUの分野でも2006年ごろからマルチコアによる性能向上に舵が切られた訳だが――それでも依然としてシングルスレッド性能も向上している*2

正直なところ、仮想化ソフトウェアは重い。何しろ「独立したハードウェア上で直接実行する」という前提で作られているx86向けのOSを無理やりソフトウェア上で動かしている訳で、Intel VTやAMD-Vなどの仮想化支援機能で軽減されるといえども、処理としては重い部類に入る。

ソフトウェアには「シングルスレッド性能が上がると高速化する」ものと「コア数を増やすと高速化する」ものの2系統があるが、経験的に仮想化ソフトウェアでは「1にシングルスレッド性能、2にコア数(ただしデュアルコア以上)」の順に効果があるように思う。

だから、まずはシングルスレッド性能が高めのCPUを選択すべきだろう。

経験的に、またベンチマーク性能的に、シングルスレッド性能は「IntelAMD*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に絞り込むのが現時点でのベターだろう。

できれば4コア以上のCPUを選択する。

ある程度シングルスレッド性能が高い場合、次に効果があるはコア数だ。現在使用している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を使いたい。

できればメモリは16GB以上積む。

ここまで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以上」という要求に当てはまることが多くなる。

大容量かつ高速なディスクを積む。性能重視ならSSDを使う。

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はまだまだ高いのである。残念。

まとめ

  • 仮想化支援機能をサポートするCPUを選択する。
  • シングルスレッド性能がそこそこ高めのCPUを選択する。
  • できれば4コア以上のCPUを選択する。
    • ホストOS用に残しておくスレッド数+(ゲストOSに割り当てるスレッド数×ゲストOSの同時実行数)」
    • 最近のOSはマルチコア環境に最適化されているので、ホストOSもゲストOSも最低でも2スレッドずつ割り当てること。
  • できればメモリは16GB以上積む。
    • ホストOSで使用するメモリ量+(ゲストOSで使用するメモリ量×ゲストOSの同時実行数)」
  • 大容量かつ高速なディスクを積む。性能重視ならSSDを使う。
    • 仮想ディスクを置いを置いた状態での、ホストOSのディスク空き容量に注意すること。
    • 性能を求めるなら仮想ディスクを含めて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インチ大画面で持ち運びに不便だったりと、なかなか良い機種に巡り合えなかった。

今までどのくらいプログラミング言語を触ってきたか(3秒で挫折したものものも含む) Ver.10

2018-06-30現在のステータス。id:eel3:20170708:1499445416 から1年経て、こうなっている。

なおCSS、HTML、XMLは除外する。人工言語ではあるけれども「プログラミング言語」という括りからは外れると思う。

よく使っている

AWK (Gawk)
単純なテキストレコードの処理にはAWKで十分。自作ツールをAWKGawk単体で実装することは皆無なものの、シェルスクリプトMakefileの中にコードを埋め込んで他のコマンドと組み合わせて使う機会は依然として多い。シェル上でワンライナーでデータ処理するときにも重宝している。これはこれで十分AWKらしい使い方だよね?
C++
ちょくちょくお仕事で使うが、本職のC++使いではない。C++11やC++14は非常に便利で、better Cでも使う価値があると思う今日このごろ。C++の標準ライブラリは、C++03の時代からC言語の標準ライブラリよりはるかに充実していたが、C++11でさらに充実しちゃってどうするのよ?*1 ラムダ式とかautoによる型推論とか使えるようになって大変ですな*2。あとSwift時代のクロスプラットフォームC++ライブラリ作者は、どうあがいてもARCから逃れられないので、C++11以降のスマートポインタは必須だ。正規表現とスレッドも標準ライブラリに加わったので、あとはソケット(低水準ネットワークAPI)をサポートしてくれないだろうか。低水準の処理を行いつつも便利な機能で実装時間を短縮できるのは便利。だけど機能多すぎ/複雑すぎなところはなんとかならないものか。強力な反面、使い手を選ぶ言語だ。
C言語
お仕事での主力言語。シンプルかつ低水準の世界が垣間見れるところが割と好き。とはいえ最近の他の言語と比較すると、シンプルすぎて安全機構が欠けていたり標準の便利機能が少なかったりするので、入門用の言語としては薦められない。にもかかわらず、プログラミング未経験者向けのC言語の本は毎年出版されている――謎だ。クロスプラットフォームなモジュール屋としては、現実解としてC89を採用しているものの、そろそろC99とかC11とか次世代の言語とか主流になってほしいところ。えっ、C++なら今からでも大丈夫だって? 冗談がきついなあ、10年以上前の秘伝のコンパイラを使ってる現場じゃあC++03だって無理無理。組み込みだとフットプリント諸々の都合でSTLすら辛いこともあるからなあ、Linuxとか普通のOSを載せる場合でも(リアルタイムOSや、そもそもOS無しの場合は……お察しください)。
DOSバッチファイル
プログラミング言語に含まれるかどうか不明だが、含めてしまう。ちょっとした自動化や、複数ツールを組み合わせて使うときのラッパーとしてよく使う。コマンドプロンプトはシバン(shebang)に対応していないので、スクリプト言語で書いたツールを起動するラッパーとしても多用している。
make (Makefile)
プログラミング言語に含まれるかどうか不明だが、DSL扱いで……いやGNU Makeはそこそこプログラミング言語的か。GNU Make 4.0はさらにプログラミング言語的だな、特にGNU Guileなところが。先人の遺産をコピペしてガシガシ書き換えるだけだった私も、FizzBuzzを切欠に本格的にGNU Makeを触り始めた。伝統的なmakeやNetBSD Make(pmake)やNMAKEとは随分異なるのね。もう素のmakeは書けないかもしれない :) まあでも私はGuileのコードが書けないので、その分だけはまだまだ素のmakeやpmakeに近いはず――といいつつも、最近書いたNMAKEも独自拡張アリアリで使ってたな。pmakeも独自拡張アリアリになるから、もはや素のmakeではMakefileを書けない身体なのか。
sed
プログラミング言語に含まれるかどうか不明だが、DSL扱いで*3。テキスト処理用。シェルスクリプトMakefileにて他のコマンドと組み合わせて使う。というか正規表現でのテキスト置換以外の機能を使った記憶が……あったな、dとiとpと=とブレースによるグループ化ぐらいだが。私のレベルではsedFizzBuzzを書けないので、sedで難しい処理を記述しないようにしている。
Windows PowerShell
v5.1が出て久しいというのに、周囲にWindows 7ユーザが多いのでv2.0で頑張らざるをえない日々が続いていたが――もう面倒なのでv5.1に切り替えた(Windows 10ユーザなので)。スクリプト言語としてのPowerShellは――またMicrosoftもエライ代物を出してきたものだ。シェルスクリプトなのにオブジェクト指向.NET Frameworkを触れてダイナミックスコープでスクリプトブロック(という名の無名関数)って、無茶しやがって……完全にプログラマ向けですな。残念なことに、コマンドプロンプトの代替という観点では、外部ツールとの親和性が微妙にイマイチだ(特に文字コードとか)。でもPowerShell内で閉じている分には問題ないので、私の手元では「Windows専用のGUI付き小ツールを作るためのスクリプト言語」か「Excel COMとか叩く用のスクリプト言語」か「Windows Serverの管理スクリプトを書くためのスクリプト言語」扱いしている。ところで、いい加減『Windows PowerShell イン アクション』クラスの最新バージョン対応の言語解説書が出版されてくれないだろうか。
シェルスクリプト (/bin/sh)
プログラミング言語に含まれるかどうか不明だが……いや、私的にはシェルスクリプトは立派なプログラミング言語だ。基本的な用途はバッチファイルと同じくちょっとした自動化や複数コマンドを組み合わせて使うときのラッパーだが、実現できる内容は遥かに多い。言語本体(?)がバッチファイルよりも高機能だし、Unixユーザランドはコマンドが充実している。その意味ではMSYSよりもCygwinで環境構築した方がマシだと思う。CygwinやMSYSでは、主要な処理をシェルスクリプトで記述しておき、bashからはシェルスクリプトを利用し、コマンドプロンプトではラッパーのバッチファイル経由でシェルスクリプトを叩く使い方をしている。ただWindows上では処理速度が妙に遅くなる点が不満だ。

あまり使っていない

flex (lex)
プログラミング言語に含まれるかどうか不明だが、DSL扱いで。字句解析用のツールという印象が強かったのだが、よく考えてみたら、flexってsed(1)のよくある使い方と同様に「正規表現でパターンマッチング --> 何らかのアクション」ということをやるためのツールだった。ただ単に、「何らかのアクション」をC言語で書けることと、flex自体ではパターンマッチングをせずに「パターンマッチングするC言語のコード」を生成することが少々風変わりなだけ。grep(1)やsed(1)その他で小ツールを実装して運用しつつ、性能が求められたらflexで専用ツール化するとか――夢が広がるな、いまさらながら。
Free Pascal
いや、お試しで触っているのだけど、微妙にDelphi/Free Pascal初心者(でも他言語経験者)向けの良い資料が少なくて難儀している。玉石混交なんだよね。いっそのこと『OBJECT PASCAL HANDBOOK―マルチデバイス開発ツールDelphiのためのプログラミング言語完全ガイド』を買ってしまおうかしら。
Io
プロトタイプベースである点を除けば、何となくSmalltalk的であるような――公式ドキュメントらしきIo Programming Guideでも影響を受けた言語として真っ先にSmalltalkが挙げられているから、あながち思い違いでもないだろう。今更ながら『7つの言語 7つの世界』のIoの章を読み終えたので、ちょっとしたコード片を書いているが……Windows版のバイナリが古いためか、リファレンス通りなのに動作しないコードに直面している。
Java
生まれて初めて触れたプログラミング言語その2。でも、もう忘れてしまった――と思っていたのだが、思い立って昔どこかから拾ってきたツールのコードに手を入れたり、急にAndroid用のサンプルアプリを触ることになったり。うーん、JDKをインストールしたり更新したりするのが面倒だ……。とりあえず、構文の見た目は保守的なC++に似ているが中身はC++とは似ても似つかない代物だということは分かっている。Javaらしいコードまであと何マイル?
Kotlin
年単位でAndroid界隈をヲチしてなかった身としては急に出てきた感があるKotlinだが、実はそうでもないんだよなあ(後から経緯を見てみると)。ちょっとした実験用に簡単なAndroidアプリをJavaで書いたので、試しにKotlinで書き直してみた。――うん、割とええ感じの言語やね。ただSwiftをつまみ食いして半年以上経ってからKotlinをつまみ食いしたので、両者の概念・機能が頭の中でごった煮になってしまった。
Objective-C, Objective-C++
時代はSwiftだと言われているけど、どっこいObjective-CObjective-C++は生きている。というかSwiftのコードにC++で書かれたライブラリを直接組み込むことができない以上、両者を繋げるグルー言語として生き残ることになるよね。最近流行の言語と比べると良くも悪くも80年代的だが、アプリケーションプログラミング用としてはC言語よりマシだし、C++ほど複雑怪奇*4ではない。そしてC言語C++で書かれた既存のライブラリをそのまま使える。Objective-Cのハイブリッドな所は好きだが、Objective-C++はハイブリッドすぎて微妙。C++のクラスとObjective-Cのクラスを、C++ラムダ式Objective-Cのブロック構文を同時に使うのは大変だ。便利ではあるんだけどね。
Python
「標準ライブラリにWAVファイル操作用モジュールがある」という理由で、小ツール作成用にちょくちょく使うようになった。ただし諸事情により、確実に使える処理系がPython 2.xなので、どれもPython 2.xを念頭にコードを書いている。Python 3.xは遠いなあ。Perl 5やRubyと比べると、Pythonではlazyなスタイルが許されず、整然とコードを記述する必要がある。その辺は、Perl 5やRubyとは随分と雰囲気が異なる。Pythonで気になるのは、インデントが必須な言語仕様であるために、シェルスクリプトに埋め込んで使うのが苦痛な点だ。Pythonだけでコードを書く分には気にならないのだけど。
QML
宣伝文句のとおり、QMLはGUIの記述に非常に向いている。それも、単に標準のUI部品(エレメント)を使うだけでなく、少し改造して使うとか、オリジナルのUI部品を作って使うとか、それらを別のアプリケーションに使いまわすとか、そういう時に威力を発揮する。あと、プロパティバインディングやレイアウトのアンカー指定など、画面サイズの変更に追随するUIを作りやすい機能も揃っている。JavaScriptでちょっとした処理も記述できる。とはいえ、やりすぎるとパフォーマンスの罠が……。少なくとも、JavaScriptでゴリゴリコードを書くのはQML的ではない。QMLは宣言的に、シンプルに書くものだ(というか、力技でロジックでゴリ押しすると、色々と罠に嵌る)。
Ruby
自作ツール実装にて、AWKの代替言語の最有力候補だった(でも最近はPythonが多いんだな、これが)。テキスト処理でも割と重宝するが、バイナリデータへの変換が絡んでくるとAWKよりもRubyを使った方が効果的だ*5。そろそろirbを電卓代わりに使うスタイルも板に付いてきた気がする。to_s(16)やto_s(2)で基数変換して表示できるところが好き。
Swift
コンパイラによる強力な型推論と型安全性のチェック」がお仕事用のメジャーな言語にまで降りてきたという点で、Swiftは静的型付け言語界のJavaScript*6だと思っている。でもユーザ数的にKotlinが「静的型付け言語界のJavaScript」ポジションを獲得しそうだなあ。割と好感が持てる言語だが、今しばらくは互換性のない言語仕様の変更が発生しそうな雰囲気なので、プロダクトオーナーとしてiOSmacOS向けアプリ開発を専業としている人以外は、Swiftを選択する際には少し慎重になった方が良いかもしれない。
Visual Basic .NET
いまさらVisual Basic .NETである。しかも2003。古いシステムの改修と移行だから、仕方ない……まあ流石にVisual Studio 2013環境に移行したんだけどね。

最近使ってないが、縁は切れてない

bash
最近はデフォルトシェルがbashな環境も多いので、自分用のツールぐらいは素の/bin/shではなくbashで書いても大丈夫な気がしてきた。shよりbashの方が遥かに便利だからなあ――PerlRuby等には負けるけど。bashスクリプトを書くときの唯一の欠点は、メジャーバージョンごとの差異や各ディストリでのビルドオプションの違いにより、同じbashという名前でも実は千差万別なところだと思う。PerlRubyのバージョンは気にするけど、これがシェルになると意外とバージョンに無頓着になってしまう。なんでだろう?
bison (yacc)
プログラミング言語に含まれるかどうか不明だが、DSL扱いで。やっぱり構文解析系統のコードを自作するのは割に合わない――だなんてうそぶきつつ、LALR法とか全く知らないままに、既存のyaccのコードを切り貼りして遊んでいるところ。まだ簡易電卓レベルだが便利さを体感しつつ、さっそくtypo 1文字で痛い目(shift/reduce)に遭った。とりあえず、flexと組み合わせた上でのエラー処理(エラーメッセージの改善)が課題だ。
C#
かつて、勉強を兼ねてC# 2.0を少し触ろうとするも未完に終わった過去をもつ私。あらためてVisual Studio 2013をインストールして、また少しだけ触ることに。言語仕様的にはC# 5.0の環境だが、使用する機能はC# 4.0相当だろうか。変数の型推論ラムダ式LINQ、デフォルト引数は便利っすね。それと.NET Frameworkの機能数は反則ものだが、所々に微妙に抽象化が行き過ぎたAPIが見られるのは気のせいだろうか? それにしても、クラスが必須ではないC言語C++に慣れてしまった弊害か、アプリケーション・メインエントリすらclass内に定義しなくてはならないC#には、なかなか慣れない。Javaよりはマシなのだが。
CASL II
生まれて初めて触れたプログラミング言語その1。何だかんだで、後でCプログラマになってからも低水準での思考ツールとして微妙に役に立っている。まあ考えるための言語であって実用言語ではないけど。仮に実用的な処理系*7があったとしても余りに命令がシンプル過ぎて悶絶するなあ、なんてFizzBuzzしてみて思った。
Go
寡作ながらもいくつか小ツールを書いてみたが、標準ライブラリが充実しているコンパイラ型言語っていいっすね。C言語に比べればC++の標準ライブラリも充実しているが、どちらかといえばプリミティブな機能が中心だ。PythonRubyばりの標準ライブラリを抱えているGoには及ばない。その辺は、やはりCプログラマ(特にCでフィルタやデーモンの類を書く層)には受けそうな言語だと思う。並列処理周り(goroutines)とかARM対応とかが気になる。ソフトリアルタイム限定だが「組み込みLinux + Goで書いたデーモン」とかどうだろう? ただメモリを食うらしいという噂がどうなったか気になる――64bit環境では解消されるという話だったようだが、32bit環境でも解消されるようになったのだろうか? 組み込みでは現時点では逆立ちしたって64bit CPUはありえないからなあ、スマホタブレット以外では。
JavaScript(クライアントサイド)
組み込み系のCプログラマも世間の流行には逆らえず、クライアントサイドJavaScriptのコード書いてみた。機器のWeb管理コンソールとか、Webアプリとの連携とか。言語仕様的に単体テストしやすい(モックへの差し替えが簡単な)ところが羨ましい。無名関数もクロージャも好きだ。
JavaScript(サーバサイド?)
Node.jsやPhantomJSが出てきたこともあり、クライアントサイドJavaScriptとサーバサイドJavaScriptの2大潮流とは別に、JavaScriptでフィルタ等の小ツールを作る文化が広まらないか少しだけ期待中。いやあ、TCP絡みのダミーサーバもどきをサクッと作るのにNode.jsが地味に便利だなあ、と。
Lua
Wiresharkのパケット解析スクリプトを書いてから数年。今度はC言語で書かれたUnixデーモンの設定ファイル用に組み込むことに。これはこれで「職業柄」の範疇、なのだろうか? Windowsのことを考えなければ、自前でライブラリをビルドしてアプリに組み込むのは結構簡単だった。
Perl 5
時々、やむをえない事情で触ることがある。だが基本的によく分からない。何というか、あの記号の羅列っぽさに中々慣れないというか、自分は余りに自由度が高すぎる言語は苦手だと気づいたというか。(言語仕様に慣れているなら)半ば使い捨てなテキストフィルタとかをさっと書くには、悪くない言語だと思うのだが。
Scheme
GaucheWindowsネイティブ環境用バイナリは実験版だが、私が触る分には何の支障もない*8ことに気づいて久しい今日この頃。『Scheme手習い』と『Scheme修行』を購入したので、とりあえずCommon LispではなくGaucheScheme)の勉強をする方向に転換しようか検討*9しているうちに何年たったのやら。Gaucheはフィルタ・ライクな小ツールの実装用としても良い感じだ。しかし最も多い利用方法はREPLを電卓代わりにすることだ*10。うーん、作業環境がMac OS XLinuxに移ったなら、大手を振ってGaucheでフィルタを書くのだが。
SQL
生まれて初めて触れたプログラミング言語その3ぐらいに位置する。MySQLを入れてコンソール用のモニタからSQL手入力で弄って遊んだことがある。組み込みの人なのでSQLとは無縁だと思っていたが、まさかTransact-SQLをちょっとだけ触ることになるとは。
Tcl/Tk
Tclは書き方を忘れた頃にテキスト処理ツールを書いている気がする。Tclは結構独特な言語だ。構文がシェルスクリプトばりに全てコマンドだったり、値が全て文字列だったり、実はリスト構造だったり、意外とTCPソケット通信が得意だったり……。それでも慣れれば結構使いやすい。意外とプロトタイピングに向いている気がする。8.6以降ではオブジェクト指向プログラミングもOKだが、それよりも例外処理用のtry末尾呼び出しの最適化用のtailcallの方が興味深い。しかし、これからメジャーになる可能性は低そうだ。Tkは……小規模なGUIツールをさくっと構築できるところは便利だが、Webアプリ全盛の時代にどれだけ訴求力があるのやら。
Vim script
少し触ってみた分には、exコマンドの拡張(=コマンドの羅列)とは気づかない程度にはプログラミング言語らしいと思う。とはいえ妙なところで嵌ったり微妙に一貫性のない部分があったりするので、その辺りで好き嫌いが別れる気がする。
XSLT
縁が切れたと思いきや、仕事でXHTMLから特定要素を抜き出す作業に使うことに。XMLからテキストレコードに変換してしまえば、後はUnix流テキストフィルタの世界が待っている。餅は餅屋というもので、定型的なXMLの変換はXSLTで記述すると楽だ。唯一気に入らないのは、xsl:sortでアルファベットの大文字と小文字を区別してソートすることができないこと。ぐぬぬぬ。

これから(また)使うかもしれない

Alloy
形式手法の中では比較的カジュアルに使えそうなので期待中。入門書も処理系も入手した。私の場合、先に何か論理型の言語をかじった方がよいのかも。
Common Lisp
2009年に勉強しようと思い立ったものの、未だに進んでいない。階乗とかハノイの塔とかiotaぐらいは書いたが、目標は「ちょっとしたツールを自作する」だ。まだ道は遠い。最近は時々CLISPを簡易電卓代わりにしている。
Coq
ソフトウェアの基礎が気になるので、処理系だけ入手。
Emacs Lisp
.emacsにコピペ」限定で。Common LispSchemeを触ったためか、何となく内容を追えるようになってきた気がしていたが、勘違いだった。
F#
OCamlは「Windows上で日本語を扱う」という視点では処理系がちょっと微妙なので、いっそのことF#に乗り換えようかと……。『実践F#』が積読状態になっている。
Forth
pForthをMinGWでビルドしたので処理系は手元にある。スタック指向の言語はいつか勉強したい。
LOGO
そういえばLOGOを触ったことがない。とりあえずUCBLogo(Berkeley Logo)だろうか? Windows上でUCBLogoばりにGUI無しで動作する処理系はないだろうか?
Object REXX
思うところがあって処理系とIBM謹製のドキュメントを入手したものの、そこから先の進展は無いまま。ReginaでClassic REXXっぽい感じで触っているからなあ。
OCaml
Common Lispを勉強するはずが、いつの間にか触っていた言語。一応、階乗ぐらいは書いた。時間が取れたらもうちょっとしっかりと勉強したいが、面倒なのでF#に移行しようか検討中。
Oz
ふと思い立ってUbuntuにMozartを入れた。『Scheme手習い』の次はCTMCP片手にOzで勉強かなあ。道は遠いな……。
PostScript
これかForthか、どちらに手を出すべきか? 悩ましい。
Processing
入門書も処理系も入手して、あとは弄る時間をつくるだけ。
Prolog
少し前に触っていた言語。『7つの言語、7つの世界』の地図の色分けプログラムには衝撃を受けた。何というか「正しい問い」を見つけられるか否かが肝なのか。この辺は、根底の部分でAlloyに通じる何かがあるように思う。ひとまず、Prologで論理プログラミングと宣言的なスタイルに慣れておけば、形式手法にて「論理で宣言的に」記述するときに戸惑いが減るのではないかと期待している。
Rust
仕事柄「C/C++の次のシステムプログラミング言語」はそれなりに興味の対象で、Go言語やD言語ほどではないが、Rustも……まあ、気にならなくはない。ちなみに、これら3言語と同列にObjective-CやSwiftが挙げられることもあるようだが、個人的見解としては、システムプログラミング言語としてのこの2言語には全く期待していない。あれは、Appleというしがらみからは逃れられないでしょうな。
Smalltalk (Squeak, Pharo)
Smalltalkは有名な古典的プログラミング言語だというのに、触ったことがない。ということでSqueakとPharoの処理系のみ準備完了。うーん、「環境」付きなのが気になる――言語を弄くる基準が「コンソール上でテキストフィルタ」という変な人種な私だからなあ。
Smalltalk (GNU Smalltalk)
個人の思想信条による理由よりSqueakとPharoにわだかまりを感じてしまう変人なので、邪道だと思いつつもコンソールでテキスト処理もOKなGNU Smalltalkも用意してみた。これで言語としてのSmalltalkの勉強に集中できる……か?
VBA (Visual Basic for Applications)
今までVBAから逃げ回っていたのだが、ついに使うことになりそうな予感。たぶん、Access VBA 8割にExcel VBA 2割ぐらい。

今は全く使っていない

Active Basic
VBScripを触りだした影響で、時々思い出しては弄くっていた。ほんの少しだけ触って放置して、すっかり忘れてからまた触る――これを繰り返していた気がする。なので毎度初めて触るのと同じ状態だった。String型をバシバシ使用 :)
bc
その昔、Windows標準の電卓アプリの代わりに使おうとして色々あって挫折した。今はirbclisp/goshで計算しているからなあ。
Clojure, Scala
JDKがなくてもJava APIを叩くスクリプトを書けるので非常に便利。Scala型推論とか、便利っすね。言語仕様はJavaよりも好みだ。とはいえ、IoT時代にJava VMベースでどこまでメインストリームに居残ることができるのか? ちょっと興味深い。サーバサイドに活路を見出すのだろうか?
COBOL
FizzBuzzするためだけにOpenCOBOL 1.0をWindows上に用意して触ってみた。なんというか、COBOLの名前と生まれた時代が示すように基幹業務(というかお金や帳簿が絡んでくるところ)向けの言語だよなあ、といった感じ。COBOL 2002のフリーフォーマットを採用するだけでも使い勝手が変わる気がしたが、世の中にはまだ広まらないのだろうか。
CoffeeScript
仕事で使う予定はない。RubyPythonその他の影響を受けているだけあり、その手のスクリプト言語っぽい感じでコードを書けるので、慣れれば素のJavaScriptで直接コーディングするよりは楽だ。しかし標準ライブラリ回りや処理系絡みの機能やサードパーティのライブラリなど、結局はJavaScriptを知らないとCoffeeScriptでコードを書けないと思う。それに生成されたJavaScriptのコードを見て「うわぁ、これあまり効率的でないなあ」と感じる時もあって、高速化が必要な部分では生成されるコードを気にしながら記述したりCoffeeScriptを諦めてJavaScriptで書くことになるので、やはりJavaScriptを知らないとマズイ。とはいえ便利なのは確かだ。CoffeeScriptのコードは即Node.jsで実行できるので、その辺りから「CoffeeScriptでテキストフィルタ」的な文化が生まれると面白いかも。気になるのはECMAScript 6の存在で、今までCoffeeScript独自の機能だった部分の多くがES6で取り込まれるので、今後ES6対応が進むにつれてCoffeeScriptの立場がどうなっていくのか、少々興味深い。
D言語 2.x
仕事柄「C/C++の次のシステムプログラミング言語」はそれなりに興味の対象で、Go言語ほどではないが、D言語も気になる存在だ。D言語シンタックスがC・C++に近いだけでなく、コーディングしている時のアプローチ・判断についても、CやC++での流儀がそこそこ通用しやすい気がする。少なくとも、Go言語でコーディングするよりは、文化的背景の違いによるモヤモヤは感じにくい。あと、標準ライブラリを使ってテキストフィルタを書いたところ、エラー処理を1〜2ヶ所のtry - catchにスッキリまとめることができて、ちょっと驚いた。throwされる例外のメッセージ文字列が、ちょうどよい塩梅の内容だったため、メッセージを変更する(いったんcatchして、再throwする)必要がなかった。ちょっと残念なのは、マルチバイト対応だが……。
Fortran
Fortran 90やFortran 95あたりは結構近代的な言語だと思う。用途次第ではC言語よりもFortranの方が遥かにマシな選択だろう。配列がらみの処理はFortranの方が得意だし、言語機能としてのモジュール化の方法はC言語には存在しない。可変長な文字列の扱いに微妙な制限がある点はマイナスな気もするが、まあ基本的に数値計算プログラム用の言語だからなあ。
GDB (GNU Debugger)
……いやGDBはデバッガとして使っているが、GDBスクリプトを書く機会は(FizzBuzz以外に)ない。勉強不足なだけかもしれない。
Groovy
JDKがなくてもJava APIを叩くスクリプトを書けるので非常に便利。動的型付け言語っぽくいくもよし、@CompileStaticや@TypeCheckedで型推論するもよし。言語仕様はJavaよりも好みだ。コンソールアプリを書く人としては、オプション引数解析用の機能を標準で持っている点で、GroovyはClojureScalaよりもポイントが高い*11。個人的には、IoT時代に「Java VMベース」の言語としてどこに活路を見出すのが、興味深く見守りたいところ。やはりサーバサイドだろうか?
HSP (Hot Soup Processor)
FizzBuzzで楽しんでみたが、何というか他言語経験者には受けが悪そうな命令体系だと思う。もっとも初心者がプログラミングという行為に深入りせずにWindows用のGUIな何かを作る分には、あの命令体系でも十分な気がしないでもない。ところで元々は「HSPで職業プログラマ的な良いコードを書くと、どんな感じになるか?」というネタを思いついて処理系を用意したのだけど、そちらは全く進展がないまま。
JScript on WSH
他人が使うテキスト処理ツールの実装に使って以来、時々触ってきた。Windows用の配布可能な小ツールを実装する時の定番言語だった。でもそろそろ潮時だろう。HTAと組み合わせてクライアントサイドJavaScriptなノリで簡易なGUIツールを実装できる点も、PowerShell + WPF + XAMLで代替できそうだ。他のメリットは「JavaScriptECMAScript)でフィルタを書ける」だったが、WSHのなかなか目的にたどり着けないオブジェクト階層にイライラするよりも、Node.jsやPhantomJSを使ったほうが精神衛生的にマシだ。
m4
その昔テキスト処理用に触ろうとして、Windows用のどの処理系も日本語の置換に何かしらの問題を抱えていたので泣く泣く諦めた。思うところがあって改めて少し触ってみたが――なるほど、確かに中毒性のある言語*12だ。
REXX
Open Object REXXの処理系を入手したのに、何故かReginaを入れてClassic REXXっぽい方向に走っていた。何というか、COMコンポーネント.NET Frameworkと無関係でいられるのなら、バッチファイルの代替としてはREXXあたりがほどよい塩梅だと感じる。しかし最近流行の言語とは随分と勝手が違うし、日本語の情報も少ない。メインフレーム以外の世界で流行る可能性は少ないだろう。
T4 Text Template
「へえ、こんなものがVisual Studioに入っていたのか。機能多すぎで色々と便利なツールを見逃しているんだな、やっぱり」と思いつつ触ってみた。テンプレート変換の用途ではピカ一だと思う。ただ処理系を手に入れる方法が「Visual Studioをインストールする」or「MonoDevelopをインストールする」なので、何となく「単体で手軽に使えるツール」ではないというイメージが……。まあC#VBで処理を記述するので、それらの処理系が必要だという面での制約なのだろう。
VBScript on WSH
JScriptほどではないが「Windows上で他人も使えるツールを書くためのLL」扱いしていた言語。Windows Server管理の関係で触っていた。というかWebで入手可能なWSHのサンプルの大半がVBScriptで書かれていたり、ADSI関連のコレクションをJScriptで舐めれなかったりして、結局は必要に駆られて使用することに。明快に記述できる文法は評価に値するが、スクリプト言語としては少々冗長だ。配列は自動拡張しないし、組み込み関数はプリミティブ気味だし、冗長気味な文法との合わせ技でコードがさらに冗長になっていく……。文法や言語仕様の詳細なドキュメントが見つからないのだが、どこにあるのだろうか?*13
秀丸マクロ
7年ほど秀丸エディタを使っていたが、マクロを書く機会はなかった。一念発起してFizzBuzzしてみて感じたのは、最近の便利な言語に慣れた身としては色々とモヤモヤ感がある言語仕様だということ(歴史的経緯的に仕方ないのだが)。とはいえちょっとした拡張ツール的なものを手軽に作れそうではあった。

*1:とはいえ、C++標準ライブラリの充実度をJavaC#.NET Framework含む)のそれと比較してはいけない。

*2:でもKotlinやSwiftを触ると、C++型推論(というか型チェック関連)は「もう少し頑張ってほしいなあ」という印象を持ってしまうから不思議だ。アレでも以前より随分と便利になったんだけどなあ。

*3:これでもsedチューリング完全な言語だと証明されているらしい。

*4:少なくともC++の言語仕様は私の手には余る。自分が把握している範囲の機能でコードを書くのは好きなのだけど。

*5:とはいえ、ついついC++を使ってしまうのだよなあ。

*6:私の認識では、JavaScriptは、第一級関数やクロージャやお仕事用のメジャーな言語に組み込まれて、少なくない人が使う契機となった言語だ。

*7:――といってもシミュレータだけど。

*8:支障がある部分を触るほど深入りするには、あと20年ぐらい掛かるのではないか?

*9:Schemeの勉強というよりも、再帰の勉強なのか?

*10:現状はirbclispかgoshの3択だ。

*11:ClojureScalaに関しては、同様の機能を私が見逃している可能性も高いのだが。

*12:m4はマクロプロセッサなのでプログラミング言語ではないはずだけど……。

*13:MSDNの資料では物足りない。もうちょっと掘り下げた内容のものが欲しい。

コメントを書かないためには文化が肝要(Re: コメントのいらないプログラムの書き方)

そろそろ言及しておこうかな。これって「コメントのいらないプログラムの書き方」ではなくて「コメントを減らす技法 初級」だよね、それも割と暗黙の前提が多めの。

以前 id:eel3:20160425:1461594142 で言及したのだけど、ソースファイル中のコメントを減らすことは、ある水準までは技術の問題だ。しかし一定水準を超えると技術ではなく(そのプログラマが所属する)環境・文化の問題となる。

コメントの無いプログラムは、「コメントを減らす」の究極形だ。なので環境や文化の問題は避けられない。

であるにもかかわらず、件の記事は技術の問題(それも割と初級のもの)しか扱っていない上に、特定の文化圏を前提とする内容を、当該文化圏について明示せずに書いてしまっている。だからはてぶのコメント欄が割と否定的な内容になっているように思う。*1

先の記事では「『コメントのいらないプログラムの書き方』のポイント3つ!」として、次の3点を挙げているのだけど:

  1. 関数や変数に適切な名前がつけられていれば、コメントはいらない
  2. 仕様がユニットテストですべて記述されていれば、コメントはいらない
  3. コメント無しで第三者にも理解できれば、コメントはいらない

(1)は半分は技術の問題で、もう半分は特定の文化圏を前提とする内容だ。(2)と(3)は完全に特定の文化圏を前提とする内容だと言える。

ところで先ほどから何度も「文化圏」と書いているけど、この記事を書いた人の文化圏は以下の別記事から推察できる。

上記の記事を読んで、若干の想像を膨らませてから、件の記事を読めば「まあ、そういう結論になるよね」と納得できる。もっとも、例えば私の所属する文化圏は随分と異なるので、件の記事を読んだところで「でも、ウチだとそうはいかないよね」という感想しか出てこない。コメントは減らせるけど、コメントがいらなくなることはないのよ、ウチの文化圏では。

コメントを減らすための技術的要素

先にも書いたが、コメントを減らすことは、ある水準までは技術の問題だ。

DRY(Don't Repeat Yourself)の視点で考えると、コメントを書くことでソースファイル中の特定のソースコードとの間に「記述の二重化」が発生するのならば、そのコメントを削除できない検討すべきだ。

だから名前(ファイル名、クラス等のモジュールの名前、関数などのサブルーチンの名前、定数や変数の名前)には適切なものを付けるべきだし、直値ではなく名前付き定数を使うべきだし、モジュールやサブルーチンのインタフェースには極力自然なスタイルを採用すべきだ。上手くいけば、下手な名前や変なインタフェースを誤魔化すための補足コメントが不要となるので、結果としてコメントを減らすことができる。

また、ソースファイルはas is、つまり「現在の姿」を映し出すものでなくてはならない。

だから「過去の姿」である変更履歴の類は「バージョン管理システムのコミットログ」や「プロジェクト管理システムのチケットのコメント」に書くべきで、ソースファイル中のコメントに書くべきではない。古いコードをコメントアウトして残しておくなどもってのほかだ。

DRYとas isの両者の観点では、プログラムに変更が発生する可能性を考慮すると、変更が発生しやすい要素についてはコメントを書かずに済むか検討すべきだろう。なぜならば、その要素にコメントが存在する(つまり「記述の二重化」が発生している)と、プログラムの変更時にas isとなるように修正すべき部分が2倍となる可能性が生じるからだ。

だから、変更が発生しやすい「式とステートメント」に密着したコメントを極力書かずに済むように「適切な名前・適切な順序・明解なインタフェース・シンプルなアルゴリズムの採用」という点を心掛けるべきだ。式やステートメントそのものの可読性を高めれば、補足のためのコメントは少なく済む。実際のところ、世の中の「コメントを書かない」系の指摘の大半は「式とステートメント」に密着したコメントをターゲットとしている。

しかし一方で、「式とステートメント」よりは変更が発生する可能性が低い要素である「データとデータ構造」については、『プログラム書法 第2版』に曰く「データの割り付けかたについての解説をつけよう」である。

プログラムに解説をつけるためにも、もっとも効果的な方法の一つは、単にデータの割り付けかたをくわしく説明する、というものである。おもな変数について、その値としてはどんなものが可能かを示し、それが変って行くようすを説明すれば、それだけでプログラムの解説は、ずいぶん進んだといってよい。

もちろん、これを『プログラム書法』の時代と同じくコメントをして記述するのか、それとも21世紀らしく別ドキュメントに記述してリンクさせるか、という点は(そのプロジェクトの実情を考慮しつつ)議論すべきだろう。

――そう、「(そのプロジェクトの実情を考慮しつつ)議論すべき」という辺りから、技術の問題よりも環境・文化の問題の割合が増えてくる。

「コメントを書かない」を実現する上での文化的障壁

まだ私の中で決定版の答えは出ていないのだが、「コメントの少ないプログラム」から「コメントのいらないプログラム」の間には、少なくとの以下の環境・文化的障壁が存在するのではないかと考えている。

  1. 使用する言語の抽象度
  2. 開発環境
  3. そのソースファイルの性質
  4. 作成者と利用者の間の「暗黙知」の問題

まず「使用する言語の抽象度」。解くべき問題に合致している抽象度の言語を用いているなら、コメントは削りやすいものだ。例えば次のスクリプトは、中身が短く簡潔で分かりやすいので、コメントは不要かもしれない。

#!/usr/bin/awk -f
# 一列目の合計を付け足す。

{
    n += $1
    print $0
}
END {
    print n
}

しかし、これがC言語で(しかも全て自前で)実装されていたらどうだろうか? 少なくとも「このプログラムは何か? 想定している入力・出力は何か?」という情報は必要だろう。

世の中、常にモダンで抽象度の高い言語が使えるとは限らない。私なんて組み込み系の人なので、C言語縛りとか「言語仕様だけC++11(標準ライブラリはC言語と同じ)」縛りとか、そういうことが結構ある。

次に「開発環境」。「使用する言語の抽象度」で挙げた例を掘り下げて考えてみよう。

適切なドキュメントが存在し、継続的にメンテナンスされていて、かつソースファイルから関連ドキュメントへの参照(またはその逆)が容易である環境であるならば、「このプログラムは何か? 想定している入力・出力は何か?」という情報をコメントに書く必要はない。どちらかと言えば、ドキュメント化しておくべき情報だろう。

しかし、そのような開発環境が整っていないのならば、ソースファイル先頭のヘッダコメント辺りに記述しておく意味は十分にあると言える。

「そのソースファイルの性質」は、例えば「自社開発アプリケーションのコンポーネントの一部で、概ね固定されたメンバーで管理しているソースファイル」なのか、「オープンソースで公開しているライブラリで、開発チームがメンテしていて、外部の複数のプロジェクトにて組み込まれるソースファイル」なのか、というような性質の違いのことだ。

メンテナンスする人が限られていて、利用者も固定されがちで、外部への露出が少ないならば、ライブラリの公開インタフェースの情報は簡素で済む。場合によっては、インタフェース仕様のドキュメントを書かずにテストコードで示すだけでも問題ないだろう。

一方で、不特定多数の人に公開されるライブラリのソースファイルでは、問い合わせを減らすためにドキュメントを充実させる傾向にある。大抵はAPIリファレンスの類が存在するはずだ。このような環境では、ソースファイル中のコメントとしてAPI仕様を記述した上でツールでドキュメントを生成する、という手法が取られることも多い。

少なくとも私は、Unixシステムコールを叩くときも、AppleiOS SDKを使う時も、APIリファレンスとサンプルコードの双方を見る。全く知らない第三者が使うかもしれないライブラリにおいては、使い方を示すのにテストコードだけでは不十分で、どうしてもAPIリファレンスが欲しくなる。この時、横着してソースファイルにAPI仕様を記述してしまうことは結構あるものだ(もちろんそこからドキュメントを生成させる訳で、別に「コメントを読め!」という主張ではないのだが)。

「作成者と利用者の間の『暗黙知』の問題」は、ソースファイルの性質とも絡んでくる話だ。

開発メンバーがある程度固定された環境では、明示的・暗黙的にかかわらず、例えば「○○の場合は××のスタイルで書け!」のようなお作法があったりする。この環境に適応すると、ソースコード中の「××のスタイル」を目にした時に「ああ、ここは○○なのだな」と容易に逆引きできるようになる。

名前の付け方やインタフェース定義の流儀など、明快かつ合理的な作法(型)が存在し、皆が作法に従っているなら、コメントを読むまでもなく暗黙のうちに理解できてしまう部分が生じるものだ。

だから「自社開発アプリケーションのコンポーネントの一部で、概ね固定されたメンバーで管理しているソースファイル」の場合、作成者も利用者も同質性が高い上に、同質性が高い人同士での利用に留まるので、暗黙知にもとづく部分の補足コメントを削ってもあまり問題にはならない。むしろ冗長性が排されるので歓迎されるかもしれない。なぜなら、彼らからすれば「当たり前の常識」で、わざわざコメントに書くほどのことではないからだ。

しかし作成者と利用者を含むメンバーの同質性が低い場合は、安易に暗黙知に頼ってコメントを削ることは危険だ。メンバーが固定されがちな傾向にあるならば、開発者向けドキュメントに暗黙知をまとめた「お作法」の項を追加して守らせる、という方法(つまり新規参加者を教育して同質性を高める方法)は有効だ。しかしそうもいかない環境では、冗長になることを諦めて、毎度毎度コメントで暗黙知の部分を補足せざるを得なくなる。

私の場合、メンバーの同質性が高いとは言えない環境での開発が多い。なので、非公開ルーチンの類であっても、名前・引数・戻り値を十二分に練り上げた上で、ダメ押しとしてルーチンの仕様をコメントで記述した上で、コメントで記述した仕様に従ってユニットテストを書いている(それが可能な程度には詳細に記述している)。数年後に他の人がメンテナになった際に、非公開ルーチンのAPIリファレンス代わりにコメントを読んでもらうことを期待しているのだ。もちろん、その程度のコストをかけることが許される環境である、という前提があるのだが。

ソースレビューによる同質性の担保も、やはりメンバーの同質性が低い環境では機能しにくい。以前、異なるバックグラウンドを持つ開発者3人をかき集めて短期集中2ヶ月でモノを仕上げる(で、終わったら即解散する)プロジェクトに関わったことがあるのだが、バックグラウンドが違い過ぎて、コーディングスタイルの統一すら無理だった(そんな時間すらなかったこともあるけど)。なので、プロジェクトの成果物は3種類のスタイルで記述されている。同一ファイルに複数のスタイルが混在していない点が唯一の救いだろう(ファイル毎には結構異なる)。このような環境では、レビューで同質性を担保することなど望めない。

しかし継続的なプロジェクトでメンバーが固定されがちなら、ソースレビューで同質性を担保することが可能だ。そして同質性が担保されるなら、暗黙知の部分を削る余地が出てくるだろう。

まとめ

適切な技法を採用することでコメントの量を減らすことが可能だが、ある水準からは環境・文化の問題となる。だから、ソースファイルからコメントを全て取り除くためには、環境・文化の問題と向き合わなくてはならない。

「コメントの少ないプログラム」から「コメントのいらないプログラム」の間には、少なくとも以下の環境・文化的障壁が存在すると考えられる。

  1. 使用する言語の抽象度
  2. 開発環境
  3. そのソースファイルの性質
  4. 作成者と利用者の間の「暗黙知」の問題

そして、環境・文化の問題は、必ずしもクリアできる代物とは限らない。

「コメントが無くても読めるようなプログラム」は「コメントの無いソースファイル」を意味しない。「ソースファイルにコメントを書かずに済むように、ありとあらゆる合法的かつ妥当な手法を駆使した結果、そのコードを共有する文化圏の基準において最小限のコメントのみが、メンテナンスしやすいスタイルで記述されているソースファイル」のことだ。

「そのコードを共有する文化圏の基準において最小限のコメント」が「少ないコメント」と「コメントなし」のどちらを意味するかは、「文化圏の基準」に依存する。だから環境・文化の違いが存在することを念頭に置いてすり合わせしないと、いつまで経っても議論は平行線のままだ。

*1:それこそ記事のタイトルが「コメントを減らす技法 初級」とかだったら、まだ荒れなかっただろう。その代わり、読まずにスルーする人は多かっただろうね。

エンジニアにとって「コミュニケーション能力」とは何を意味するのか?

世の企業も求むる「コミュニケーション能力を持つ社員」といふものを、エンヂニアリングの現場でも求めてみむとてするなり。

「コミュニケーション能力」という言葉は実に曖昧で、10人いれば5〜6パターンぐらいは異なる解釈が出てきそうな代物だ。この文章では、ソフト屋さんを含む「エンジニアリングの現場にいる技術者」に求められるコミュニケーション能力の実体について、基本的なところを述べてみたい。

(ただし私はプログラマなので、ソフト屋寄りの見解となる)

コミュニケーション能力を「社会生活を営む人間の間で知覚・感情・思考を伝達するための能力」であると仮定した上で、掘り下げてみよう。

コミュニケーション能力を獲得するための第一歩は、経験の言語化だ。一般に、人間はことばという共通のフレームを用いて情報を伝達する。経験を言語化できなければ、経験を相手に伝えることができないし、また相手の言葉を理解する(ことばを経験にマッピングする)こともできない。

一般的に、経験の言語化は幼少期に家族などの特定の親しい人との会話によって形成され始めるらしい。ただ、発達障害(とくに自閉症)の場合、会話によってこの辺の能力を形成していくのが難しい傾向にあるようだ(ではどうすればよいか、という点については色々と研究されている模様)。

経験を言語化できていると仮定すると、次の論点は、発達心理学でいう一時的ことばと二次的ことばだろう。

一時的ことばは、同じコンテキストを持つ親しい相手と1対1でコミュニケーションする際に用いることばだ。経験の言語化が、幼少期に家族などの特定の親しい人との会話によって培われるために、一時的ことばは「お互いについての知識・経験を共有している」という前提のものとなる。だから、不完全な表現であっても相手は共有する知識・経験から補完して理解することができる。

二次的ことばは、未知の人も含む不特定多数の人とコミュニケーションする際に用いることばだ。この場合、「お互いについての知識・経験を共有している」という前提は成立しない。そのため、情報を発信する側は、発信する情報を理解するために必要な情報を全て言語化し、整理した上で、表現する必要がある。同時に、情報を受信する側は、内容を理解するために必要な情報を全てことばの文脈から抽出し、個々の情報の関連付けを行うことで、理解する必要がある。

二次的ことばは、スピーチのように不特定多数にたいして語る場面だけでなく、チームミーティングでの進捗報告や、業務手順書の作成のように、「ある程度共通するバックグラウンドを持つが、『家族などの特定の親しい人』ほどはお互いについての知識・経験を共有していない、特定少数の人々」との情報伝達でも用いられる。お互いに共有している知識・経験が限られているため、情報を発信する際には、相手が内容を正しく理解するに足るだけの情報を提供しなくてはならない。

社会人に求められるコミュニケーション能力は、「二次的ことばによる情報の伝達がスムーズに行える」ということである。つまり、自分が伝えたい内容について相手側の知識・経験が乏しい場合でも正しい情報を伝達できることであり、その逆に相手が発信する場合にも会話・文章ということばだけから正しい情報を理解できることである。

要するに「話が面白い」とか「会話が弾む」などの面で評価が高い人であっても、それが「一時的ことばによる身内間での情報伝達」に留まるのならば、それは「社会人に求められるコミュニケーション能力」を兼ね備えていることを意味しない。

さて、二次的ことばによるコミュニケーションでは、情報を全てことば(相手の話した内容や、文章に書かれている内容)だけから理解していく必要がある。これを実現するには、ことばに含まれる単語を正確に理解し、単語を連結している文法を正確に理解する必要がある。また自分自身が発信側となる場合には、単語を正確に用いつつ、単語を連結する文法を正確に用いなくてはならない。

社会人(というか職場)でのコミュニケーションで用いられる単語には、日常でも用いられる単語だけではなく、その環境に応じた専門用語・業界用語の類も含まれる。エンジニアのコミュニケーションにおいては専門用語が多用される傾向にあるので、各専門用語の意味を正確に理解して用いる必要がある。

なぜ専門用語が多用されるのか? お互いに「相手はその専門用語を知らない」という前提でコミュニケーションしようとすると、内容が冗長になりすぎて、全てを伝えるのに時間がかかりすぎてしまうからだ。だから、専門用語として厳密に意味を定義した上で、双方とも定義された意味を理解しているという前提で情報伝達を行うのである。

例えるなら……バリカタを知らない人にバリカタという言葉を使わずに「あのラーメン屋のバリカタは柔らかすぎでバリカタじゃない」ということを伝えようとするようなものである。バリカタのラーメンを食べたことがない人は、バリカタを「通常よりもかなり硬い麺」に置き換えて説明されたとしても、そもそもバリカタの硬さを理解していないので、「あのラーメン屋のバリカタという名の『バリカタよりも柔らかい麺』」がどの程度の硬さなのか類推できない。だから、単に「通常よりもかなり硬い麺」に置き換えるのでは駄目で、相手が食感を覚えている食べ物のうち、「バリカタに近い硬さのもの」と「あのラーメン屋のバリカタという名の『バリカタよりも柔らかい麺』に近い硬さのもの」を探り出して提示しなくてはならない。しかし身をもってバリカタを知っている人同士ならば、バリカタで十分通じるものだ。だから時には「話をする前に、今から『本当にバリカタのラーメン』と『バリカタという名のバリカタよりも柔らかい麺のラーメン』を食べ比べに行こうぜ!」という方法も有効だ。ことばで説明するよりは早く済むだろう。でも食べ比べの時間は余分にかかるから、結局は、バリカタを知っている人同士の方が情報伝達が早く済むことになる。

エンジニアが用いる専門用語は、その職場・チームといった環境に特異のものだけでなく、他の環境のエンジニアを含むもう少し広い範囲(例えば業界)にて共通言語として用いられる専門用語もある。例えばソフトウェア・エンジニアの場合、基本情報技術者試験に出てくるような用語は、専門用語の中でも広い範囲で用いられるものだ(スレッドという単語が通じるソフト屋さんは少なくないはず)。そういった公知の専門用語をどれくらい正しく理解して使用できているか――という点もエンジニアのコミュニケーション能力に含まれてくる。

ただし専門用語の知識の有無は人によって異なるので、情報を発信する側は、文脈によって専門用語を用いるか否かを切り替える必要がある。例えば、新入社員と会話する時と同期の社員と会話する時では、前者よりも後者の方が専門用語の割合が多いはずだ。

また、エンジニアのコミュニケーションでは、抽象度の高い事象や、大きくて一度に理解できない事象を取り扱うことが多い。そのため、同じ情報を後から何度でも参照できるように、会話だけでなく「文章を用いた二次的ことばによるコミュニケーション」が多用される傾向にある。文章によるコミュニケーションでは、会話でのコミュニケーションでは可能な「その場のニュアンスで不完全な表現を補完する」ということが不可能なので、単語の内容や、単語を連結する文法を、会話の場合よりも正確に用いなくてはならない。

その上で、文章だけでは抽象度の高い事象を正確に伝達できない可能性があるため、時には適切な図表を用いる必要もある。事象によっては数式で表現することもあるだろう。ソフト屋の場合は、時にソースコードという人工言語にて他のエンジニアとコミュニケーションを図ることもある。

つまるところ、文章と図表を用いて情報を正確に発信できること、文章と図表から情報を正確に読み取ることができること、この2点である。この辺りは、大学教育でのレポートや論文の作成にて発信側を、レポート・論文作成の際の文献調査にて読み取り側を研鑽するように思う(で、その前提となる「普通の文章の読み書き能力」は義務教育で学ぶ……と書いてしまって大丈夫だろうか?)。

まとめると、エンジニアに求められる「コミュニケーション能力」とは、以下を兼ね備えていることである。

  1. 二次的ことばによる情報の正確な伝達をスムーズに行うことができる。
  2. 文章と図表による情報の正確な伝達をスムーズに行うことができる。
  3. 専門用語を正しく理解して使用することができる。

基本的に、エンジニア同士のコミュニケーションでは「正確な情報をスムーズに」という点が重視される。エンジニアが取り扱う事象の大半は、抽象度の高いものや、大きくて一度に理解できないものなどの、複雑な事象だ。複雑なものを取り扱うには正確な情報が必要であるし、複雑なものを取り扱うことに集中するためにも余分なコスト(「情報が不正確かもしれないので再検証する」という時間的ムダなど)を払いたくないものだ。

特に、簡単な事象が過去に解決されてきた積み重ねの結果、エンジニアリングの現場にて取り扱う事象が高度化している昨今では、複雑な事象にチームで取り組むことが多い。『人月の神話【新装版】』のブルックスの法則にて言及されているように、チーム内で相互コミュニケーションが求められる場合、コミュニケーションの労力は人数nの時「n(n - 1) / 2」に比例する。このような本質的問題を抱える状況において、「情報が不正確である」とか「情報は正確だが、読み解くのに苦労する」などの偶有的問題まで発生したら、進むものも進まない。それゆえに、エンジニアは「正確な情報をスムーズに」という点を重視する傾向にある。

だから、世間一般で言われるコミュ障(雑談が苦手・苦痛なタイプ)でも、業務上必要な情報を正確かつスムーズに伝えられるのなら、大抵は問題ない。もっとも、コミュ障ゆえに対人関係の構築に不慣れな場合、業務上必要な情報を伝達する際に相手が把握している情報量をうまく推測できないために、「どこまで情報を付与すれば相手が理解できるか/どこまで省略しても相手が理解できるか」を見誤ることはある*1

まあ、最初から上記3項目を全て高いレベルで身に着けている新卒社員は少ないだろうから新人教育やOJTで最低限鍛えるぞ――というのがジャパニーズ・トラディショナル・カンパニーの伝統行事なのだが、しかし会社にも「許容できる教育コスト」に上限がある。だから、新卒採用にて「コミュニケーション能力のある人=上記(1)と(2)がある程度の水準に達している人」を求めるのだ、その辺の能力を正しく計測できて採用できているか否かは別として*2

*1:難しいよね。

*2:多分できていない。

実はオブジェクト指向ってしっくりこないんです?

――いや、自己分析するに、「オブジェクト指向」ではなく「特定のオブジェクト指向プログラミング言語」がしっくりこないことがあるんだな、これが。

具体的には、触ったことのある言語ではJavaC#だ。この2つの「しっくりこなさ」具合からすれば、C++の方がマシだ(もっとも、C++は別の部分で好きになれない面があるのだが*1)。

C#は経験が浅すぎるので、Javaについて。Javaは、ここ数年の間にちょっとしたコンソールアプリの実装とAndroidアプリの開発で使用した。興味深いことに、コンソールアプリを書いた際は「しっくりこなさ」全開だったのだが、Androidアプリを書いた時にはその手の違和感はなかった。

この違いは何だろうと考えて、ふと気づいた。おそらく私は、クラスの定義を強要されることに違和感を感じる体質なのだ。

私のプログラマとしての土台(第一プログラミング言語)はC言語だ。C言語にはクラスはない。次に使うことが多いC++は、クラスを定義するも定義しないも活殺自在な言語だ。JavaScript/JScriptVBScriptも、クラスを定義する/しないは比較的自由だ。小ツール実装で時々使うRubyPythonは、ちょっとしたスクリプトであれば構文の見た目として「クラス定義なんて知らないよっメソッド定義だけだよ!」風に記述できる。

これらの言語のうち、C言語C++のアプリケーションメインエントリは関数だ。JavaScript/JScriptVBScriptRubyPythonでは、必須構文としてのアプリケーションメインエントリ的なものはない*2

つまり、C言語を除けば、コードの見た目として「必要になったらクラスを定義するが、必要なければ使わない(関数/メソッドを単独で定義して用いる)」というスタイルで記述することが可能な言語に慣れている(そしてC言語にはそもそもクラスが存在しない)。

しかしJavaC#は、例えばC++にてmain関数と2つのサブ関数で記述できるような小ツールであっても、クラスを定義した上でmainメソッドを記述しなくてはならない。この時点で、何というか「この程度のことでクラスを持ち出すなんて……」と思ってしまうのだ。

その一方で、Androidアプリのような複雑な*3ソフトウェア、特にGUIアプリケーションというオブジェクト指向プログラミング的アプローチに向いているソフトウェアでは、クラスを用いる意味もメリットもあるので、違和感を感じることがない。とはいえ、時としてクラスに属しないメソッドを書きたくなることはある。C++ならば名前空間に直接属する関数として、Objective-CならC言語流のファイルによるモジュール分割を用いたデータ結合の関数として定義するだろう、ちょっとした汎用のユーティリティ・メソッドだ。

別の視点から考察すると、どうも私は「構造体(=レコード型)」の影響を強く受けていて、「何らかの関連がある複数のデータ+それらを操作する専用のルーチン=オブジェクト」という意識が強い。C言語で構造体を使用してややオブジェクト指向プログラミングっぽいアプローチをすることもあるが、その場合、「何らかの関連がある複数のデータ」を一まとめにする必要があるならば構造体にまとめるが、必要なければまとめない。

つまり、心理的に「クラス≒抽象データ型≒レコード(複数のフィールド)と操作用ルーチン」という意識がある。抽象データ型として扱うのが妥当ならばクラスにするが、それ以外の用途――例えばJavaJavaScriptのMathクラスのような使い方――でのクラスの採用に消極的というか、クラス以外の代替機能を使おうとする個人的傾向がある。

仮にMathクラスのようなものをC++で再実装するならば*4、個人的にはクラスではなく名前空間を使用して、関数や定数をひとまとめにするだろう。

このようなアプローチは、例えばC++で採用するには問題ない。しかし、慣れ故にJavaC#でも採用しようとしてしまい、メソッドをクラス内でしか定義できないことに気づき、ついついもやもやしたものを感じてしまう。

現実には、プログラミング言語には「言語仕様」という制約がある訳で、その制約の元では、例えば「『C++名前空間』のような機能がないので、代わりにクラスを使う」などのアプローチは合法的だろう。だから、もやもやしたものを感じてしまう必要など全くないのだが……。

結局のところ、慣れの問題なのだ。クラスのない世界での生活が長く、また必要に応じてクラスを定義するか否かを切り替えられる言語に慣れてしまっていて、クラスが必須な言語には慣れていないので、違和感を感じるのだろう。

蛇足:クラスが必須な言語に違和感を感じる一方で、CやC++のような関数が必須な言語(ステートメントを関数の中にしか記述できない言語)には違和感を感じないあたり、どう考えても慣れの問題以外の何者でもない。

*1:好きにはなれないが、better Cとして使い方をわきまえている分には便利な言語だと思う。

*2:もしかしたら、私が知らないだけで、アプリケーションメインエントリ的なものがあるかもしれない。しかし、そうだとしても、必須ではない(何らかの構文として記述する必要がない)だろうから、その言語のユーザからすれば「無いも同然」だ。

*3:テキストフィルタよりもGUIアプリの方が複雑だ、という意味での「複雑な」である。

*4:標準ライブラリに用意されている、C言語由来のcmathの数学関数は無視するものとする。