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

2020-06-30現在のステータス。
https://eel3.hatenablog.com/entry/2019/06/30/000000 から1年経て、こうなっている。

なおCSS、HTML、XMLはひとまず除外する。人工言語ではあるけれども「プログラミング言語」という括りに含められるか否かは議論が分かれる気がする。*1

よく使っている

AWK (Gawk)
単純なテキストレコードの処理はAWKで十分間に合う。今の時代、自作ツールをnawkやGNU awk単体で実装するのは苦行すぎて*2皆無なものの、シェルスクリプトMakefileAWKのコードを埋め込むなどして他のコマンドと組み合わせて使う機会は依然として多い。シェル上でワンライナーでテキスト処理する時にも重宝している。これはこれで十分AWKらしい使い方ではないだろうか?
C++
ちょくちょくお仕事で使うが、本職のC++使いではない。C++11やC++14は非常に便利で、better Cでも使う価値がある。C言語使いからすると、C++03時代よりも充実度が進んだ標準ライブラリ、ラムダ式、autoによる型推論――などなど、便利で羨ましい限りだ*3。あと、Swift時代のクロスプラットフォームC++ライブラリ作者は、どうあがいてもARCから逃れられないので、C++11以降のスマートポインタは必須だ*4正規表現とスレッドも標準ライブラリに加わったので、あとはソケット(低水準ネットワークAPI)をサポートしてくれないだろうか。低水準の処理を行いつつも便利な機能で実装時間を短縮できる点は便利で、少なくともシステムプログラム言語としての利点だと思う。だけど機能多すぎ/複雑すぎなところはなんとかならないものか。強力な反面、使い手を選ぶ言語だ。
C言語
お仕事での主力言語。シンプルかつ低水準の世界が垣間見れるところが割と好き。とはいえ最近の他の言語と比較すると、シンプルすぎて安全機構が欠けていたり標準の便利機能が少なかったりするので、入門用の言語としては薦められない。にもかかわらず、プログラミング未経験者向けのC言語の本は今でも出版されている――謎だ。クロスプラットフォームなモジュール屋としては、今までC89を採用してきたものの、いい加減そろそろC99とかC11とか次世代の言語とか使いたい。でも古いVisual Studioもサポートしたいから、C99すら2023年まで使えない*5。悲しいなぁ。
DOSバッチファイル
プログラミング言語に含まれるかどうか不明だが、含めてしまう。ちょっとした自動化や、複数ツールを組み合わせて使うときのラッパーとしてよく使う。コマンドプロンプトはシバン(shebang)に対応していないので、スクリプト言語で書いたツールを起動するラッパーとしても多用している。意外と色々なコマンドが用意されているので、単純にそれらを叩く分には十分だが――言語機能がショボいので、バッチファイルでifやforのような制御構文系コマンドが必要になってきたら、何か決定的に間違えていないか、考え直すようにしている。
Kotlin
本格的にAndroidアプリ開発に関わるようになったのがGoogle I/O 2017直後の過渡期なので、JavaよりもKotlinでの経験値の方が多い。モダンな「強い静的型付け」の、割とええ感じの言語やね。ただ、使い始めが「Swift 3をつまみ食いして半年以上経ってからKotlinをつまみ食いした」みたいな経緯だったこともあり、未だに両者の概念・機能が頭の中でごった煮になっている。それと、NDK絡みの作業が多いので、C++11/14・Java 7/8・Kotlinを行ったり来たり。泣けるぜ。Swiftもそうだが、最近のメジャーな「強い静的型付け」の言語は「開発環境込み」で高い生産性とコードの安全性を両立させる方向に進んでいる気がする。
make (Makefile)
プログラミング言語に含まれるかどうか不明だが、DSL扱いで……いやGNU Makeはそこそこプログラミング言語的か。GNU Make 4.0はさらにプログラミング言語的だな、特にGNU Guileなところが。GNU MakeとNMAKEが主力で、稀にNetBSD Make(pmake)を使うが、いずれも独自拡張アリアリだ。もう素のmakeではMakefileを書けない :)
Python
Python 2.xで実装した小ツール群をPython 3.xに移植した。Pythonではlazyなスタイルでのコーディングが許されず、整然とコードを記述する必要がある。その辺は、Perl 5やRubyとは随分と雰囲気が異なる。気になるのは、インデントが必須な言語仕様であるために、シェルスクリプトに埋め込んで使うのが苦痛な点だ。Pythonだけでコードを書く分には気にならないのだけど。
sed
プログラミング言語に含まれるかどうか不明だが、DSL扱いで*6。テキスト処理用。シェルスクリプトMakefileにて他のコマンドと組み合わせて使う。というか正規表現でのテキスト置換以外の機能を使った記憶が……あったな、dとiとpと=とブレースによるグループ化ぐらいだが。私の技術レベルではsedFizzBuzzを書けないので、sedで難しい処理を記述しないようにしている。
Swift
コンパイラによる強力な型推論と型安全性のチェック」がお仕事用のメジャーな言語にまで降りてきたという点で、Swiftは静的型付け言語界のJavaScript*7だと思っている。でもユーザ数的には、Kotlinが「静的型付け言語界のJavaScript」ポジションなのかもしれない。割と好感が持てる言語だが、知識が中途半端にKotlinとごった煮になっているので、ついうっかりif式を書こうとしてコンパイルエラーになったり、「varval」と「varlet」の振る舞いの差異につまづいたりしてしまう((Kotlinのvalは「再代入不可の変数」だ(定数はconstで定義する)。Kotlinのプリミティブ型以外のデータ型はclass(つまり参照型)なので、valで定義した変数を破壊的操作する行為は割と普通だと思う。一方でSwiftのletは定数であるし、値型が中心の言語仕様である影響かstructやenum(つまり値型として振る舞うデータ型)が多用されるので、letで定義した変数を破壊的操作できるケースとできないケースが生じる。))。そろそろ互換性のない言語仕様の変更が発生し難くなってきた感じかなあ。
Windows PowerShell
時代はPowerShell Coreらしいが、現行のWindows 10でデフォルトで利用できるv5.1に留まったままである。スクリプト言語としてのPowerShellは、オブジェクト指向.NET Frameworkを叩けてダイナミックスコープでスクリプトブロック(という名の無名関数)と、無茶でピーキーで完全にプログラマ向けな代物だ。Microsoftもよくもこんなエライ代物を出したものだ。残念なことに、コマンドプロンプトの代替という観点では、外部ツールとの親和性が微妙にイマイチだ(特に文字コードとか)。でもPowerShell内で閉じている分には問題ないので、私の手元では「Windows専用のGUI付き小ツールを作るためのスクリプト言語」か「Excel COMとか叩く用のスクリプト言語」か「Windows Serverの管理スクリプトを書くためのスクリプト言語」扱いしている。ところで、いい加減『Windows PowerShell イン アクション』級の最新バージョン対応の言語解説書を出版してくれないだろうか。
シェルスクリプト (/bin/sh)
プログラミング言語に含まれるかどうか不明だが……いや、私的にはシェルスクリプトは立派なプログラミング言語だ。基本的な用途は、バッチファイルと同じくちょっとした自動化や複数コマンドを組み合わせて使うときのラッパーだが、実現できる内容は遥かに多い。言語本体(?)がバッチファイルよりも高機能だし、Unixユーザランドはコマンドが充実している。その意味では、WindowsではMSYSよりもCygwinで――いやむしろWSL(Windows Subsystem for Linux)で環境構築すべきだろう。Cygwinでは、主要な処理をシェルスクリプトで記述しておき、bashからはシェルスクリプトを利用し、コマンドプロンプトではラッパーのバッチファイル経由でシェルスクリプトを叩く使い方をしている。ただWindows上では処理速度が妙に遅くなる点が不満だ。

あまり使っていない

Java
生まれて初めて触れたプログラミング言語その2。実のところ、職業プログラマとして本格的に使用することは一生ないと思っていた。Androidアプリ開発も、Kotlin採用後に本腰入れて関わるようになったので、Kotlinメインだ。だが、なぜかぬるい感じに時々Javaのコードを触っている。先にコレクションの操作方法が充実した他の言語を学んでからJavaを本格的に触るようになったので、Java 8以降のStream APIが使えないと身体が拒否反応を示す。少なくとも、構文の見た目こそ保守的なオブジェクト指向プログラミング・スタイルで書かれたC++に似ているけど、中身はC++とは似ても似つかない代物だということは体感している。
Objective-C, Objective-C++
時代はSwiftだと言われているけど、どっこいObjective-CObjective-C++は生きている。というかSwiftのコードにC++で書かれたライブラリを直接組み込むことができない以上、両者を繋げるグルー言語として生き残ることになるよね。最近流行の言語と比べると良くも悪くも80年代的だが、アプリケーションプログラミング用としてはC言語よりマシだし、C++ほど複雑怪奇*8ではない。そしてC言語C++で書かれた既存のライブラリをそのまま使える。Objective-Cのハイブリッドな所は好きだが、Objective-C++はハイブリッドすぎて――C++のクラスとObjective-Cのクラスを、C++ラムダ式Objective-Cのブロック構文を同時に使うのは大変だ。便利ではあるんだけどね。
Perl 5
時々、やむをえない事情で触ることがある。だが基本的によく分からない。何というか、あの記号の羅列っぽさに中々慣れないというか、自分は余りに自由度が高すぎる言語は苦手だと気づいたというか。(言語仕様に慣れているなら)半ば使い捨てなテキストフィルタとかをさっと書くには、悪くない言語だと思うのだが。
QML
宣伝文句のとおり、QMLはGUIの記述に非常に向いている。それも、単に標準のUI部品(エレメント)を使うだけでなく、少し改造して使うとか、オリジナルのUI部品を作って使うとか、それらを別のアプリケーションに使いまわすとか、そういう時に威力を発揮する。あと、プロパティバインディングやレイアウトのアンカー指定など、画面サイズの変更に追随するUIを作りやすい機能も揃っている。JavaScriptでちょっとした処理も記述できる――とはいえ、やりすぎるとパフォーマンスの罠が……。少なくとも、JavaScriptでゴリゴリコードを書くのはQML的ではない。QMLは宣言的に、シンプルに書くものだ。力技でロジックでゴリ押しすると、色々と罠に嵌る言語だ。
Ruby
自作ツール実装にて、AWKの代替言語の最有力候補だった(でも最近はPythonが多いんだな、これが)。テキスト処理でも割と重宝するが、バイナリデータへの変換が絡んでくるとAWKよりもRubyを使った方が効果的だ*9。そろそろirbを電卓代わりに使うスタイルも板に付いてきた気がする。to_s(16)やto_s(2)で基数変換して表示できるところが好き。
SQL
生まれて初めて触れたプログラミング言語その3ぐらいに位置する。組み込みの人なのでSQLとは無縁だと思っていたが、まさかTransact-SQLを少しだけ触ることになるとは。Androidアプリ絡みでSQLiteに触れることもあるが、あれはAndroidXのRoom経由じゃないと身体が受け付けない。
Visual Basic .NET
Visual Basic .NET 2003で書かれたコードを時々メンテ中。流石に開発環境はVisual Studio 2013移行したけど。

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

bash
最近はデフォルトシェルがbashな環境も多いので、自分用のツールぐらいは素の/bin/shではなくbashで書いても大丈夫な気がしてきた。shよりbashの方が遥かに便利だからなあ――PerlRuby等には負けるけど。bashスクリプトを書くときの唯一の欠点は、メジャーバージョンごとの差異や各ディストリでのビルドオプションの違いにより、同じbashという名前でも実は千差万別なところだと思う。PerlRubyのバージョンは気にするけど、これがシェルになると意外とバージョンに無頓着になってしまう。なんでだろう?
C#
かつて、勉強を兼ねてC# 2.0を少し触ろうとするも未完に終わり、数年後にあらためてVisual Studio 2013をインストールして少しだけ触った*10けどほんの少しだけで終わった過去をもつ私。変数の型推論ラムダ式LINQ・デフォルト引数は便利だなあと思っていたら、いつの間にかC# 8.0になってKotlinやSwiftに見られる流行を取り入れてますな。おっちゃん、付いてくのが大変だよ。.NET Frameworkの機能数は反則ものだが、所々に微妙に抽象化が行き過ぎたAPIが見られるのは気のせいだろうか? それにしても、クラスが必須ではないC言語C++に慣れてしまった弊害か、アプリケーション・メインエントリすらclass内に定義しなくてはならないC#には、なかなか慣れない。
CASL II
生まれて初めて触れたプログラミング言語その1。何だかんだで、後でCプログラマになってからも低水準での思考ツールとして微妙に役に立っている。まあ考えるための言語であって実用言語ではない。仮に実用的な処理系*11があったとしても余りに命令がシンプル過ぎて悶絶するなあ、なんてFizzBuzzしてみて思った。
Free Pascal
お試しで触っているのだが、微妙にDelphi/Free Pascal初心者(ただし他言語の経験者)向けの良い資料が少なくて難儀している。玉石混交なのだ。いっそのこと『OBJECT PASCAL HANDBOOK―マルチデバイス開発ツールDelphiのためのプログラミング言語完全ガイド』を買ってしまおうかしら。
Go
寡作ながらもいくつか小ツールを書いてみたが、標準ライブラリが充実しているコンパイラ型言語っていいっすね。C言語に比べればC++の標準ライブラリも充実しているが、どちらかといえばプリミティブな機能が中心だ。PythonRubyばりの標準ライブラリを抱えているGoには及ばない。その辺は、やはりCプログラマ(特にCでフィルタやデーモンの類を書く層)には受けそうな言語だと思う。並列処理周り(goroutines)とかARM対応とかが気になる。ソフトリアルタイム限定だが「組み込みLinux + Goで書いたデーモン」とかどうだろう? ただメモリを食うらしいという噂がどうなったか気になる――64bit環境では解消されるという話だったようだが、32bit環境でも解消されるようになったのだろうか? 組み込みでは現時点では逆立ちしたって64bit CPUはありえないからなあ、スマホタブレット以外では。
JavaScript(クライアントサイド)
クライアントサイドJavaScriptのコードを書いたこともあったけど、スキル的にはもう何もかも賞味期限切れだよなあ。ECMAScript 2015以降を使うなら、それ以前とはコードのスタイルから変えないと不味そう。
JavaScript(サーバサイド?)
Node.jsやPhantomJSが出てきた頃は、クライアントサイド以外でもJavaScriptが使われる文化が広まる可能性があるのか気になったものだが……結局どうなっているの? 何か処理系もNode.jsに収束してきた印象が。
Lua
Wiresharkのパケット解析スクリプトを書いたことも、C言語で書かれたUnixデーモンの設定ファイル用に処理系を組み込んだこともあった*12。あれから数年経ったが、今はどんな感じなんだろう?
Scheme
GaucheWindowsネイティブ環境用バイナリは実験版だが、私が触る分には何の支障もない*13ことに気づいて久しい今日この頃。『Scheme手習い』と『Scheme修行』を購入したので、とりあえずCommon LispではなくGaucheScheme)の勉強をする方向に転換しようか検討*14しているうちに何年たったのやら。Gaucheはフィルタ・ライクな小ツールの実装用としても良い感じだ。しかし最も多い利用方法はREPLを電卓代わりにすることだ*15。うーん、作業環境がmacOSLinuxに移ったなら、大手を振ってGaucheでフィルタを書くのだが。
Tcl/Tk
Tclは書き方を忘れた頃にテキスト処理ツールを書いている気がする。Tclは結構独特な言語だ。構文がシェルスクリプトばりに全てコマンドだったり、値が全て文字列だったり、実はリスト構造だったり、意外とTCPソケット通信が得意だったり……。それでも慣れれば結構使いやすい。意外とプロトタイピングに向いている気がする。8.6以降ではオブジェクト指向プログラミングもOKだが、それよりも例外処理用のtry末尾呼び出しの最適化用のtailcallの方が興味深い。しかし、これからメジャーになる可能性は低そうだ。Tkは……小規模なGUIツールをさくっと構築できるところは便利だが、Webアプリ全盛の時代にどれだけ訴求力があるのやら。
XSLT
よく考えてみたら生まれて初めて触れたプログラミング言語その4ぐらいに位置する言語だった。縁が切れたと思いきや、仕事でXHTMLから特定要素を抜き出す作業に使うことがあったり……。XMLからテキストレコードに変換してしまえば、後はUnix流テキストフィルタの世界が待っている。餅は餅屋というもので、定型的なXMLの変換はXSLTで記述するべきか。唯一気に入らないのは、xsl:sortでアルファベットの大文字と小文字を区別してソートすることができないこと。ぐぬぬぬ。

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

Alloy
形式手法の中では比較的カジュアルに使えそうなので期待中。入門書も処理系も入手した。私の場合、先に何か論理型の言語をかじった方がよいのかも。
bison (yacc)
プログラミング言語に含まれるかどうか不明だが、DSL扱いで。やっぱり構文解析系統のコードを自作するのは割に合わない――だなんてうそぶきつつ、LALR法とか全く知らないままに、既存のyaccのコードを切り貼りして遊んでみた。簡易電卓レベルだが便利さを体感しつつ、さっそくtypo 1文字で痛い目(shift/reduce)に遭った。とりあえず、flexと組み合わせた上でのエラー処理(エラーメッセージの改善)が課題だ。
Common Lisp
2009年に勉強しようと思い立ったものの、未だに進んでいない。階乗とかハノイの塔とかiotaぐらいは書いたが、目標は「ちょっとしたツールを自作する」だ。まだ道は遠い。最近は時々CLISPを簡易電卓代わりにしている。
Coq
ソフトウェアの基礎が気になるので、処理系だけ入手。
F#
OCamlは「Windows上で日本語を扱う」という視点では処理系がちょっと微妙なので、いっそのことF#に乗り換えようかと……。『実践F#』が積読状態になっている。
flex (lex)
プログラミング言語に含まれるかどうか不明だが、DSL扱いで。字句解析用のツールという印象が強かったのだが、よく考えてみたら、flexは「sed(1)のよくある使い方」と同様に「正規表現でパターンマッチング --> 何らかのアクション」という内容を記述するためのツールだった。ただ単に、「何らかのアクション」をC言語で書けることと、flex自体ではパターンマッチングをせずに「パターンマッチングするC言語のコード」を生成することが少々風変わりなだけ。grep(1)やsed(1)その他で小ツールを実装して運用しつつ、性能が求められたらflexで専用ツール化する――とか考えたけど、普通にgrep(1)やsed(1)を使う方が高速だった。
Forth
pForthをMinGWでビルドしたので処理系は手元にある。スタック指向の言語はいつか勉強したい。
Io
プロトタイプベースである点を除けば、何となくSmalltalk的であるような――公式ドキュメントらしきIo Programming Guideでも影響を受けた言語として真っ先にSmalltalkが挙げられているから、あながち思い違いでもないだろう。今更ながら『7つの言語 7つの世界』のIoの章を読み終えたので、ちょっとしたコード片を書いているが……Windows版のバイナリが古いためか、リファレンス通りなのに動作しないコードに直面している。
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というしがらみからは逃れられないでしょうな。
VBA (Visual Basic for Applications)
今までVBAから逃げ回っていたのだが、ついに使うことになりそうな予感。たぶん、Access VBA 8割にExcel VBA 2割ぐらい。
Vim script
少し触ってみた分には、exコマンドの拡張(=コマンドの羅列)とは気づかない程度にはプログラミング言語らしいと思う。とはいえ妙なところで嵌ったり微妙に一貫性のない部分があったりするので、その辺りで好き嫌いが別れる気がする。

今は全く使っていない

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する)必要がなかった。ちょっと残念なのは、マルチバイト対応だが……。
Emacs Lisp
.emacsにコピペ」限定で。Common LispSchemeを触ったためか、何となく内容を追えるようになってきた気がしていたが、勘違いだった。
Fortran
Fortran 90やFortran 95あたりは結構近代的な言語だと思う。用途次第ではC言語よりもFortranの方が遥かにマシな選択だろう。配列がらみの処理はFortranの方が得意だし、言語機能としてのモジュール化の方法はC言語には存在しない。可変長な文字列の扱いに微妙な制限がある点はマイナスな気もするが、まあ基本的に数値計算プログラム用の言語だからなあ。
GDB (GNU Debugger)
……いやGDBはデバッガとして使っているが、GDBスクリプトを書く機会は(FizzBuzz以外に)ない。勉強不足なだけかもしれない。
Groovy
JDKがなくてもJava APIを叩くスクリプトを書けるので非常に便利。動的型付け言語っぽくいくもよし、@CompileStaticや@TypeCheckedで型推論するもよし。言語仕様はJavaよりも好みだ。コンソールアプリを書く人としては、オプション引数解析用の機能を標準で持っている点で、GroovyはClojureScalaよりもポイントが高い*16。個人的には、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用のどの処理系も日本語の置換に何かしらの問題を抱えていたので泣く泣く諦めた。思うところがあって改めて少し触ってみたが――なるほど、確かに中毒性のある言語*17だ。
Smalltalk (Squeak, Pharo)
Smalltalkは有名な古典的プログラミング言語だというのに、触ったことがない。ということでSqueakとPharoの処理系のみ準備完了。うーん、「環境」付きなのが気になる――言語を弄くる基準が「コンソール上でテキストフィルタ」という変な人種な私だからなあ。
Smalltalk (GNU Smalltalk)
個人の思想信条による理由よりSqueakとPharoにわだかまりを感じてしまう変人なので、邪道だと思いつつもコンソールでテキスト処理もOKなGNU Smalltalkも用意してみた。これで言語としてのSmalltalkの勉強に集中できる……か?
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で舐めれなかったりして、結局は必要に駆られて使用することに。明快に記述できる文法は評価に値するが、スクリプト言語としては少々冗長だ。配列は自動拡張しないし、組み込み関数はプリミティブ気味だし、冗長気味な文法との合わせ技でコードがさらに冗長になっていく……。文法や言語仕様の詳細なドキュメントが見つからないのだが、どこにあるのだろうか?*18
秀丸マクロ
7年ほど秀丸エディタを使っていたが、マクロを書く機会はなかった。一念発起してFizzBuzzしてみて感じたのは、最近の便利な言語に慣れた身としては色々とモヤモヤ感がある言語仕様だということ(歴史的経緯的に仕方ないのだが)。とはいえちょっとした拡張ツール的なものを手軽に作れそうではあった。

*1:HTML5 + CSS」の組み合わせなら、チューリング完全の疑惑があったり、JavaScript使わずにCSSでWebチャットを作った猛者がいたりと、色々と怪しいのだけど。

*2:「独立性の高い単体のツールを実装する」という視点では、現代ではAWKよりも便利な言語が山ほどある。

*3:しかし標準ライブラリの充実度をJavaC#.NET Framework含む)と比較したり、型推論まわりをKotlinやSwiftと比較してはいけない。以前よりも随分と便利になったのだけど、だけど、隣の芝生を見てしまうと、うーん……。

*4:SwiftではC++の機能を直接呼び出すことができないので、Objective-Cでラッピングして利用することになる(インタフェースはObjective-Cで、内部実装はObjective-C++)。この時、Objective-Cクラスのインスタンス変数として「C++クラスのインスタンスを保持するスマートポインタ」を持つ構成にしておくと、Objective-Cクラスのインスタンスがdeallocされる時に、スマートポインタ経由でC++クラスのインスタンスもdeleteされる。

*5:本格的にC99がサポートされ始めたのはVisual Studio 2013以降だ。それよりも前のバージョンのEOLだが、Visual Studio 2010が2020年7月、Visual Studio 2012が2023年1月となっている。

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

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

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

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

*10:言語仕様的にはC# 5.0の環境だが、ライブラビまわりはC# 4.0相当だったはず。

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

*12:Windowsのことを考えなければ、自前でライブラリをビルドしてアプリに組み込むのは結構簡単だった。

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

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

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

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

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

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

「IT業界の『奥が深い症候群』」について:バッドノウハウと「奥が深い症候群」再考

IT業界には「奥が深い症候群」って言葉があってな

使いにくいシステムを使いこなすことに喜びを感じて、使いこなせない人間を見ると罵倒する

そういう人が幅を利かせていつまでも改善がされない

そんな症状だ

https://anond.hatelabo.jp/20200603093957

ソフトウェア・エンジニアが使うツールには、使い勝手の良くないものが割と存在する。その辺の事情に関してはバッドノウハウと「奥が深い症候群」で語られている内容が詳しいのだが、必ずしもそれが全てではない

例えばGitは、まあ15年ほど開発が続けられているツールではあるが、それでもどちらかと言えば後発のバージョン管理システムだ。しかしオープンソースの有名どころバージョン管理システムを歴史順に「CVSSubversion・Git」と並べた時、Gitは最後発なのにも関わらず、使いこなせないというユーザは最も多いように見える。

このGitの「使いこなせない」という現象は、「バッドノウハウと『奥が深い症候群』」における議論とは少し方向が違うように思う。

ユーザとしてCVSSubversion・Git全ての利用経験があるが、CVSSubversionはちょっと説明を受けただけで「大雑把な使い方の全体像」を理解できた一方で、Gitを使い始めた当初の感想は「どう使えば良いのか分からない」だった。

CVSSubversionは集中型バージョン管理システムという特徴より、実のところ使い方のスタイルは概ね定まっている。メンバー全員がアクセスできるサーバ上にリポジトリを置いておき、各メンバーの端末にファイルをチェックアウトし、編集し、リポジトリにチェックインする。大抵はこのようなスタイルに固定される。

一方でGitは、使い方のスタイルが定まっているとは言い難い。リモートリポジトリを1つ利用する従来のSubversionっぽいスタイル、リモートリポジトリを持たないスタイル、複数のリモートリポジトリを参照するスタイル等々、割と色々な使い方ができるように設計・実装されている。だからGitでバージョン管理したいなら、まずは自分の環境に合ったスタイルを模索しなくてはならず、模索するためにはGitの内側(表面的な使い方ではなく、操作の背景や内部メカニズム)に目を向ける必要がある。

エリック・S・レイモンドが『The Art of UNIX Programming』で述べたUNIX思想に「ポリシーをメカニズムから分離せよ」というものがある。ポリシー(物事を行うときの原則)は時代と場所で変化する不安定なものであるから、ツールを作る際にはメカニズム(機能)だけ提供するようにしておいて、ユーザには自身のポリシーに沿った形でメカニズムを利用してもらう――という考え方だ。

CVSSubversion・Gitのいずれも、基本的にはメカニズムのみを提供する。しかしCVSSubversionはその性質上、大まかな利用スタイルが決ってしまう。私はこれを「暗黙のポリシー」と呼んでいる。

ソフトウェア・エンジニアが使うツールの「使い勝手の悪さ」の一部は、ポリシーがない状態でメカニズムを渡された際の、自由度の高さに起因する。明示的でも暗黙的でも、ポリシーさえあるなら、ひとまずそれに従って使えばよい。使っていくうちに、段々と分かってくるようになるものだ。しかしポリシーが無いと、自由過ぎて困惑してしまう。第一の関門がこれだ。

次に、どうにかこうにかポリシーを定めたら、今度は「ポリシーに沿うようにメカニズムを利用するには、どうすれば良いか?」という問題が生じる。うまい具合にポリシーに合わせるために、メカニズムそのものへの理解を深める必要がある。この点は「ツールの学習コストの高さ」として表面化する。

さらに言えば、「自身のポリシーに沿った形でメカニズムを利用」するということは、言い換えれば「運用でカバー」なのだ(ただしマンパワーではなく「ユーザの使い込み度」というある種の技術力で解決する、という方向だが)。つまりツール利用の熟練度が低い間は運用トラブルは必至だと言える。だから誰しも、commit前にpullしたり、commit後のpushを忘れたりしてしまうのだ。

こんな風に、自由度の高い、メカニズムのみを提供するツールがトラブルを引き起こす例は枚挙にいとまがない。というかUNIXの系譜にあるUnix-like環境のCLIからして、初心者お断りの芳醇な香りが漂う代物だ。

それなのに、GitもUnix-like環境も一部のソフトウェア・エンジニアを惹きつけて止まないし、相変わらずメカニズムのみ提供するツールは作られ続けている。なぜだろうか?

要は、自由度の高さは諸刃の剣なのだ。扱い方をしくじれば大怪我するが、うまくコントロールできれば強力な武器となる。例えばUnix-like環境のCLI信者の大半は、この「熟練度が高くなるにつれて、メカニズムを自在にコントロールできるようになり、飛躍的に生産性が向上する」という体験の中毒患者なのだ。

この辺は(Lispの話題ではあるが)以下の記述が正鵠を射ている。

Lisp系言語が、プログラマに大きな力を与えるという意味で「自由である」ってところにはあまり議論はありませんね。議論はその有り余る自由が利点か欠点かってことでしょう。

Lisperの立場から言えば、その自由は、限界に当たった時にその限界をハックしてさらにその先へ進むためにどうしても必要なことなんです。ハックだから、それが恒久的解決になるわけじゃない。正しい解決は処理系の改善や、もしかするとコンピュータサイエンスの進歩を待たなくちゃならないかもしれない。でも、「今、とりあえず」自分を引き止めている限界を乗り越えたい、と思ってしまうわけですね。

Lisp:よくある正解

ツールの自由度の高さは、現実の開発においてソフトウェア・エンジニアの武器となりうる(偶にしくじって怪我をするが)。だから今でもメカニズムのみを提供するツールが作られ続けているし、熱心なユーザはツールの熟練度上げに熱中している。

ただし熟練度上げの中毒になると「奥が深い症候群」を発症しやすいし、時に自分よりも熟練度の低い人を見下しがちである。ついでに、新しいツールが出てきた時に「今まで使ってきたツールの熟練度」惜しさより否定的な態度をとってしまうことがある。

ところで、ポリシーよりもメカニズムを重視するのは、限られた分野の話だ。一般的な分野の一般的な人は、自由度の高いツールよりも「簡単な操作で、ポリシーに従った結果を得られる」ツールを好む。ソフトウェア・エンジニアが他の分野の人と交流する際には、この辺のスタンスの違いを念頭に置くべきだろう。

コードに愛を注ぐ

愛のないコードは好きになれない。

機能仕様は満たしているものの、中身はネット上のコードのコピペで、複数のコピペ元のコーディングスタイルが絡み合った、使われない変数とステートメントがあちこちに残ったままのスクリプト

正常系での見た目の振る舞いには問題がないものの、使い勝手の悪いインタフェースで、異常系が考慮されてなく、リソース管理の観点が抜け落ちていてメソッド呼び出しの度にメモリリークが発生するクラス。

「とりあえず『現物合わせ』で動く代物を拵えた」という作り方をされたために、開発担当者の手元では動作するものの、中身を軽く見ただけで「これ多分、他の端末だとまともに動かないよね」と分かってしまうPCアプリケーション。

そのようなコードを目にする度に、陰鬱とした気分になる。

どれもこれも、コードに注ぐ愛が足りていない。足りないから、歪な成長を遂げて奇形が出来上がってしまう。奇形はやがて怪物となり、創造主を脅かすだろう。

怪物は退治しなくてはならない。しかし私はハンターではない、という思いがある。プログラマは誰だって創造主のままでいたいのだ。

だから、奇形や怪物に遭遇すると、不本意ながらハンターの役割を果たさざるをえないことに気づいて気が落ち込むし、奇形ばかり生み出す人と仕事をするとストレスで気が滅入ってしまう。

それでは、自分自身はどうかというと、どうにも自己愛が強すぎて、自己流で癖の強いコードになってしまう。愛とは名ばかりの自己陶酔にすぎず、結果として愛の足りないコードが出来上がってしまう。

個人的な理想は「空気のようにその場に溶け込むコードを書くこと」で、言語で例えるなら「C++C++らしく、KotlinはKotlinらしく、PythonPythonらしく」書きたいし、GUIフレームワークなら「CocoaCocoaらしく、MFCMFCらしく、QtはQtらしく」書きたい。なのだけど、どうにも他の言語やフレームワークの流儀が混ざったり、機能の好き嫌い故に当該言語からすると独特なアプローチを採用してしまうなどして、癖のあるコードになってしまうのだ。

他人のコードを読むのを怠り、殻に閉じこもり続けてきた弊害だろう。

コードに愛を捧げるのは容易い――愛だろうと自己満足だろうと、捧げるのは簡単だ。しかし、それがコードに届いて注がれるか否かは別の話だ。コードに愛を注ぐことの、なんと難しいことか。

ところで、ひとつ重大なことを述べておくと、プログラマという人種は、ソースコードという「記号を並びたてたヨクワカラナイ代物」にたいして、美しさを見出したり愛を感じたりするヘンタイもとい少々特殊な趣味の人たちである。昨今の各国におけるプログラミング教育必修化の流れと、その副作用として発生するかもしれない「プログラマが増える」という側面*1に、私は人類の業の深さを感じずにはいられないのである。

*1:基本的には「『コンピュータとプログラミングがどういうものか、またどのようにして現実世界の問題に適用すればよいか、理解している人』を増やす」というのが必修化の目的だろう、と理解している。

コンソールアプリを自作する時のお約束

長らく――というほどではないが、そこそこの期間をコンソールアプリの自作に費やしてきたので、コツのようなものを晒してみる。

対話型を避け、他のツールとの連携を重視する

今、この時代にコンソールアプリを新たに作成する場合、ユースケースの大半は「自分用」ないし「自分を含む開発者向け」だと考えたほうがよいだろう。すなわち「ユーザ≒それなりに腕に覚えのある人」である。

もしも利用者層をもう少し広げたいのならば、コンソールアプリは避けて、GUIアプリにするべきだろう。

利用者層を限定できるとなると、コンソールアプリを設計する際の方針が明確になる。

  • 対話型を避ける。
  • 他のツールとの連携を重視する。

対話型ツールは、(そのツールの目的・機能次第だが)それはそれで有用ではあるものの、作業の自動化が難しく、他のツールとの連携もとりにくい。つまり、スケールし難い。

対話型を諦めると、ツールの作り次第ではあるものの、作業自動化や、他のツールとの連携への道が開けてくる。この分野は「Unixのコマンド文化」という先達がいるので、その知見を取り入れればよい。

他のツールとの連携については、「作成したツールの拡張性」ということ以外に重要な点がある。それは「他のツールで可能なことは他のツールに任せて、他のツールでは実現が難しい部分だけ開発する」ということだ。これによって、開発するツールがコンパクトになり、開発に要する期間の短縮や、潜在的に入れ込んでしまうだろうバグ件数の低減に結びつく。特に、楽をするための自分用ツールでは、開発や不具合対応に時間を割きたくないので、こういった視点は重要だ。

念のため追記:当然ながら「スケールし易い対話型ツール」は作成可能だ*1。しかし、その作成コストは「『Unixのコマンド文化』を踏襲したコンソールアプリ」よりも大きくなる傾向にあると思う。

既存のツールの組み合わせで解決できないか考えてみる

いざ、コンソールアプリ開発へ――と一歩踏み出すにあたり、コードを書き出す前に、まず既存のツールを組み合わせることで解決できないか、考えてみるべきだろう。

bashなどのシェル上でワンライナーで実現できる内容なら、コードの規模は大きくないので、シェルスクリプト化してコードを整理するのに苦労することはないはずだ。

仮にシェルスクリプトの暗黒面にうんざりする事態に直面した場合は、シェルスクリプトの身の丈に合わない大きさの複雑なツールを作ろうとしていないか、またはシェルスクリプトには不向きなことを実現しようとしていないか、振り返ってみるべきだろう。もしかしたら、他の言語を使うべきかもしれない。

足りない部分だけ開発する

既存のツールの組み合わせでは解決できないので、新たにコンソールアプリを書くことに決めたとする。

ツールの内容次第だが、既存のツールでは解決できない、不足している部分のみツール化して実装することを検討するべきだ。限られた部分のみ実装することにより、開発の手間が減ることになる。

また、ツールを実装する際も、処理系付属の標準ライブラリや、サードパーティのライブラリを積極的に活用して、本当に足りない部分だけを実装できないか、検討した方が良いだろう。

より高水準なプログラミング言語を採用する

新たにコンソールアプリを書くにあたり、より高水準で便利な言語を使えるのなら、それらを用いるべきだ。

Unix環境でいうなら、いきなりC言語C++で実装するのではなく、PerlPythonRubyなどの有名どころのスクリプト言語の採用を検討したほうがよい。実際、この3言語はテキストフィルタの実装で用いられることも多い。Unix環境では、依存関係の都合でPerl 5やPython 2 or 3の処理系がインストール済みでも不思議ではない。最近ではGo言語も良い選択肢となるだろう。

Windowsでは、PowerShellが最有力候補だろうか。WSH(とそのデフォルトの言語であるVBScript/JScript)は、今となっては少々古めかしいので、あまりお勧めできない。

解くべき課題によっては、より特化した専用言語を選択してもよいだろう。例えばUnix環境では鉄板のsedAWK、対話型操作を自動化するExpect、グラフ表現用のデータ記述言語DOTなどは、それ単体でアプリを実装するのは厳しいかもしれないが、「他のツールと組み合わせる」という前提では、驚くほど効果を発揮することがある。Prologなども有用だろう。

もちろん、スクリプト言語を採用する場合は、開発したコンソールアプリの利用者の環境を考慮する必要がある。コンパイル型言語であっても、ランタイムライブラリの有無を考慮する必要があるかもしれない。継続的に使われるツールなら、メンテナの育成(自分以外にその言語で開発できる人がいるか?)も考えておかなくてはならない。

そういう配慮は必要なものの、対応を間違わなければ、より高水準なプログラミング言語を採用することは、非常に魅力的だといえる。

複数の引数に対応できるようにする。

作成するコンソールアプリが引数としてファイルを指定する類のものだった場合は、可能なら一度に複数のファイルを指定できる仕様にするのが望ましいだろう。

作成したツールをコマンドラインから使用する時、もし複数のファイルを処理したいなら、ファイル名としてワイルドカードを用いたくなるかもしれない。Unix環境では、ワイルドカードを展開するのはシェルの役割だが、展開された結果を引数で受け取り処理するのはアプリ側の責務だ。一度に複数の引数を処理するように実装しなくてはならない。

複数の引数に対応できるならば、xargsやfind(1)のオプション-execと組み合わせた際に、当該ツールの呼び出し回数が最小化される。プロセス生成回数が減るので、その分だけ処理の高速化が期待できるだろう。

# ファイル1つ
./mycmd foo.txt

# ファイル2つ以上
./mycmd foo.txt bar.txt
./mycmd foo.txt bar.txt baz.txt

# ワイルドカードで複数指定
./mycmd *.txt

# find(1)との組み合わせ
find . -type f -regex '.*\.txt$' -exec ./mycmd {} +

実行結果(終了状態)を返す。最低でも成功/失敗を返す。

アプリの実行結果を終了状態(Unixシェルなら$?Windowsコマンドプロンプトなら%ERRORLEVEL%で確認できる値)でも返すようにしておくべきだろう。これによって、シェルスクリプトやバッチファイルにて実行結果と制御構文を組み合わせることが可能となる。

通常は、成功したら0を、失敗や異常終了では1を返すようにしておけば済むことが多い。時にはgrep(1)の「見つかったら0、見つからなかったら1、失敗したら2」のような凝った方法が望ましいこともあるが、そういうケースは稀である。

# 成功ならOKと表示される
./mycmd foo.txt && echo OK

# 失敗ならNGと表示される
./mycmd bar.txt || echo NG

※実のところ「成功したら0、失敗したら1」という流儀は、あくまでも「多くのシステムで通用するお約束」でしかない。つまり厳密に言えば移植性に欠けている。可搬性求めるのなら、CやC++では0や1などの直値ではなくEXIT_SUCCESSEXIT_FAILUREを使えばよいだろう。ところで他の言語では、これらのマクロに該当する定義は用意されているのだろうか? Perl 5なら、POSIXモジュールにEXIT_SUCCESSEXIT_FAILUREが用意されているようだが。

フィルタの既定の入力元は標準入力にする。オプションで複数ファイルからの入力に対応する。

作成するコンソールアプリがフィルタの類ならば、既定の入力元は標準入力にするべきだろう。でなければ、パイプライン経由で他のアプリの出力を受け取ることができない。

同時に、引数経由で複数ファイルからの入力にも対応すべきだろう(「複数の引数に対応できるようにする」を参照」)。

# 引数指定されたファイルを処理
./myfilter foo.txt

# 標準入力経由でも受け付ける
./myfilter <foo.txt

# 標準入力経由で受け付ける => パイプで結合できる
cat foo.txt | ./myfilter
./mycmd foo.txt | ./myfilter

# ファイル2つ以上にも対応
./myfilter foo.txt bar.txt baz.txt

フィルタ以外では、引数の個数が0個だった時の振る舞いを考える。

フィルタの場合、引数の個数が0個の場合には、標準入力からの入力を読もうとするだろう。この振る舞いは一般的なものだ。

では、例えばchmod(1)のように「フィルタではなく(標準入力を読むことはなく)、引数にファイルが指定される」という使い方のコマンドでは、引数の個数が0個の場合にどう振る舞うべきだろうか?

この問いの答えは、なかなか難しい。例えば、実行結果を成功と失敗のどちらにするべきだろうか? 今のところ「そのコマンドのユースケース次第」という玉虫色の回答しかできない。コマンドの出力についても、Unix文化的には「何も出力しない」が正しい気がする一方で、ツールの性格次第では「ヘルプメッセージを出力しても良いかも」と思うこともある。

少なくとも、1つだけ確実に言えることは……引数の個数が0個だった時の振る舞いを考える必要がある、ということだ。

時に異常な入力にたいして「エラー処理して特別扱い」ではなく「異常値を事前に取り除く」などを検討する

1回限りの使い捨てワンライナーならともかく、何度も繰り返し利用されるスクリプトならば、入力にたいしてある程度ロバストに作っておくのは悪くない習慣である。

問題は「ある程度」の基準をどこに置くかだろう。

例えば、フィルタをパイプで数珠つなぎしてテキストレコードを処理する、Arrayをメソッドチェーンで処理する、コレクションをLINQで処理する――という風に、複数のデータをストリームで処理するコードを書いた時、その中に「異常値を見つけたらユーザに問い合わせる」などの特別扱いの機能を挿入するのは難しい。どうしてもエラー処理の部分にて一旦ストリームが途切れてしまう。

この場合は「黙って異常値を取り除く」や「異常値を正常範囲に丸める」といった対応の方が、ストリームを途切れさせずに間に挿入しやすいことが多い。

だから、ケースバイケースではあるが、時には異常値の扱いとして「事前に取り除く」や「正常範囲に丸める」などを検討しても良いだろう。

フィルタの既定の出力先は標準出力にする。オプションで単一ファイルへの出力に対応する。

作成するコンソールアプリがフィルタの類ならば、既定の出力先は標準出力にするべきだろう。でなければ、パイプライン経由で他のアプリに流し込むことができない。

同時に、オプション引数で出力先ファイルを指定できるようにもするべきだろう。

# 出力先未指定の場合は、標準出力に垂れ流す
./myfilter foo.txt

# 標準出力に垂れ流す => リダイレクトできる
./myfilter foo.txt >foo_mod.txt

# 標準出力に垂れ流す => パイプで結合できる
./myfilter foo.txt | ./mycmd >foo_mod_mod.txt

# 出力先を指定できる
./myfilter bar.txt >bar_mod_1.txt
./myfilter -o bar_mod_2.txt bar.txt
cmp bar_mod_1.txt bar_mod_2.txt && echo IDENTICAL

フィルタ以外では、時に複数のファイルへの出力に対応するか否か検討する。

フィルタなどで頻出するオプション「-o」で指定できる出力先は大抵1ヶ所だけである。この仕様でも大抵は問題ないが、しかし「常に問題ない」という訳ではない。ユースケース次第では、複数の入力ファイルにたいして複数の出力先ファイルを指定可能とするべきだろう。

例えば、以前extpcmというツールを作ったことがある。このツールはRIFF WAVEファイルからPCMデータ部分のみを抽出するPythonスクリプトだ。

このツールを作った理由は「大量のRIFF WAVEファイルからPCMデータを取り出す」という作業を自動化するためだった。

ところで、取り出したPCMデータは元ファイルごとに個別に出力する必要があった。extpcmは出力先をオプション-oで1つしか指定できなかった。そのため、コンソールやスクリプト上でfor文を使って1ファイルごとにextpcmを呼び出さなくてはならなかった。これでは1ファイルごとにPythonの処理系の起動とスクリプトの読み込みが発生してしまい、大量のファイルを扱う際に処理完了まで時間がかかってしまう。

そこで、別ツールとして、複数の出力先ファイルを指定できるextpcmsを作った。extpcmsは入力ファイル数と同じ数の出力先ファイルを要求する仕様だ。extpcmsのおかげで、大量のファイルを扱う場合でもPythonの起動とスクリプトの読み込みが1回だけで済むようになり、処理時間が短縮された。

以上の例のように、ユースケースとして「複数のファイルへの出力」が重要である場合には、教条的にオプション-oの一般的挙動に拘るのではなく、使い勝手の観点で「複数の出力先ファイルを指定する」という仕様を検討しても良いだろう。

エラー出力等は標準エラーに出力する。

特に、既定の出力先が標準出力であるフィルタの類では、エラーメッセージなどは標準エラー出力に垂れ流すべきだろう。でなければ、フィルタで加工された出力とエラー出力がごちゃ混ぜになってしまう。

出力の機械可読性を考慮する。

特にフィルタにおいて顕著だが、自分が作成したツールの出力をパイプ経由で他のツールに流し込むケースを考慮するなら、時に「ユーザが見やすい出力」よりも「他のツールにて手軽に分解・加工しやすい出力」を選択するべきだろう。

フィルタを自作してみれば分かるが、ヒューマン・リーダブルで例外則が含まれる入力を解析するのは面倒だ。コードを書く側としては、定型的で例外則の少ない入力の方が扱いやすい。

だから、ユースケース次第ではあるが、作ろうとしているツールの出力を他のツールで利用することが多いのならば、機械可読性を考慮した出力とする方が好ましいだろう。

ヘルプメッセージ出力用のオプションを付けておく。

慣習に従って、オプション「-h」や「/?」などでヘルプメッセージを出力する機能を追加しておくことは、良い習慣だと言えるだろう。なぜなら、自作ツールの数が増えるに従って、よほど頻繁に使用するコマンドでもない限り、当該コマンドを開発した自分自身ですら使い方を忘れてしまうからだ。

./myfilter -h
# => 'usage: myfilter [-h] [-o file] [-v] [file [file ...]]'

./myfilter -v
# => 'myfilter 1.0.0'

オプション引数解析用の機能を用いる。

多くのモダンな言語では、オプション引数を解析する標準的な仕組みが用意されているものだ。そのような環境では、オプション引数の解析で困ることはない。

しかし、言語によっては、今でもオプション引数解析の標準的な仕組みが無くて困ることがある。

  • Tcl:多分tcllibのcmdlineを使うことになる。準公式のライブラリだから、メンテナンスとか大丈夫だと思いたい。
  • C/C++Unix環境(MinGW含む)限定ならgetopt(3)を使えばよい。Windows向けないしクロスプラットフォームのツールは――まずC/C++以外の言語を検討すべきだと思う。
  • Java、Kotlin:サードパーティのライブラリを使うことに抵抗があるなら、シェルスクリプトなどでラッピングすること。処理の中核はJava/Kotlinで実装するのだが、その際に固定の順番・個数の引数を取るようにしておく。その上で、オプション解析などの前処理をラッパスクリプト側で実施してから、Java/Kotlinで実装されたコア部分に固定の順番・個数で引数を渡せばよい(例としてXSLT-PFのJava実装を参照)。

*1:例えばVimとか。

シェルスクリプトでJavaScript文字列リテラル用のエスケープ

JavaScript文字列リテラル用のエスケープ」という表現は微妙だけど、要はサーバサイドでJavaScriptを動的生成する時に、文字列リテラルとして任意のパラメータを出力したい場合のエスケープ処理。

先日(というか先月)書いたHTMLエスケープ用のシェルスクリプトは、通常のHTMLエスケープ用だ。HTMLの文法にて特別扱いされる最小限の文字をエスケープするので、HTML要素の内容(Content)ないし属性値として使用する文字列に対して適用するのが正しい。

eel3.hatenablog.com

サーバサイドではJavaScriptを動的生成することも時々ある。大抵はJavaScriptの文字列リテラルとして任意のパラメータを出力するスタイルだ。このケースでのエスケープは一般的なHTMLエスケープとは異なる。

  1. 出力したJavaScriptの文字列リテラルJavaScriptコード中で以下のプロパティ/メソッドに適用していて、かつHTML要素として解釈されたくない場合は、HTMLエスケープする。
    • HTMLDocument.write()
    • HTMLDocument.writeln()
    • HTMLElement.innerHTML
  2. JavaScriptの文字列リテラルとして許容される内容にエスケープする。
    • 「\」を「\\」に置換する。
    • 「'」を「\'」に置換する。
    • 「"」を「\"」に置換する。
    • 制御文字をエスケープシーケンスに置換する(例えば改行を「\n」に置換する等)
  3. 追加の処理。
    • JavaScriptのコードの出力先が何らかのタグの属性値の部分であるなら、属性値全体をHTMLエスケープする。
    • JavaScriptのコードの出力先がscriptタグの中であるなら、「</script>」を含まないようにする。
    • JavaScriptのコードの出力先がCDATAの中であるなら、「]]>」を含まないようにする。

「</script>」や「]]>」を含まないようにするにはどうするか? JavaScriptの文字列リテラルの範囲内で「</script>」や「]]>」と等価な別の表現にすればよい。例えばUnicodeエスケープシーケンスに置き換えるのだ。JavaScriptの文字列リテラルでは、 "]]>" と "\u005D\u005D\u003E" は等価だ。

変換元テキストがUTF-8であるという前提で、試しにシェルスクリプトエスケープ処理を実装してみた。escape-jsstrという名前だ。

#!/bin/sh

PATH=/bin:/usr/bin:/usr/local/bin

readonly HEX2='[0-9A-F][0-9A-F]'
readonly SPC='[[:blank:]][[:blank:]]*'

exec iconv -f UTF-8 -t UTF-16BE ${@+"$@"} |
    od -A n -t x1 -v |
    tr '[a-z]' '[A-Z]' |
    sed "s/\\($SPC$HEX2\\)$SPC\\($HEX2\\)/\\1\\2/g
         s/$SPC/\\\\u/g" |
    tr -d '\n'

面倒なので全てUnicodeエスケープシーケンスに変換している。

hexdump(1)を使用できる環境なら、もう少し簡単に実現できる。

#!/bin/sh

PATH=/bin:/usr/bin:/usr/local/bin

exec iconv -f UTF-8 -t UTF-16BE ${@+"$@"} |
    hexdump -v -e '"\\" "u" 2/1 "%02X"'

とはいえ、いつもhexdump(1)を使えるとは限らない。LinuxmacOSFreeBSDなどを使う分には困らないが、Cygwinでは標準では含まれない(別途util-linuxを入れる必要がある)。商用UNIXは触ったことがないが、やはり怪しいようだ。

echo(1)ライクに引数の文字列をエスケープするシェルスクリプトはこんな感じ。

#!/bin/sh

exec echo -n ${@+"$@"} | escape-jsstr

HTMLエスケープネタと同様にとりあえず作ってみたけど、需要あるのかなあ?

終わりに

最終的には、こんな感じになった。

シェルスクリプトでHTMLエスケープ

PHPPerlでのHTMLエスケープの話はよく目にするけど、そういえばシェルスクリプトでは見かけないなあ、なんて思った次第でございます。

動的Webコンテンツの先駆けであるCGIの普及期でさえ言語としてPerlが大々的に使われていたわけで、そもそもサーバサイドの言語としてシェルスクリプトが使われる機会は圧倒的に少ない。となればシェルスクリプトでのHTMLエスケープネタが見つからないのも当たり前というもの。

しかし無いとかえって作りたくなるもので、変換元テキストがUTF-8であるという前提で、試しにシェルスクリプトで最小限のHTMLエスケープを実装してみることにした。

まずはHTMLエスケープ処理の本体。escape-htmlという名前だ。

#!/bin/sh

PATH=/bin:/usr/bin

exec sed 's/&/\&amp;/g
          s/</\&lt;/g
          s/>/\&gt;/g
          s/"/\&quot;/g
          s/'"'"'/\&apos;/g' ${@+"$@"}

標準入力ないし引数指定したファイルに対して最小限のHTMLエスケープを行い、標準出力に書き出す。この出力結果は、HTML要素の内容(Content)ないし属性値としてのみ使用できる(ただし属性値として使用する場合は前後をクォート文字で囲むこと)。

escape-htmlを元に、echo(1)ライクに引数の文字列に対してHTMLエスケープを行うシェルスクリプトを書くと、こんな感じだろうか?

#!/bin/sh

exec echo ${@+"$@"} | escape-html

引数を全てそのままechoに渡しているので、オプション `-n' で末尾の改行を付加しないスタイルになることが多いと思う。

とりあえず作ってみたけど、果たして需要はあるのだろうか?

終わりに

最終的には、こんな感じになった。

犯人はWindows Update……か?

ここまであからさまに「Windows Updateが原因でPCガー」的な場面に遭遇したのは初めてである。

5~6年ほど前に購入したノートPC Dell Vostro 2521が2台転がっていて、実験用に使おうとしたのだ。

2台ともWindows 7からのアップグレードでWindows 10化していて、現時点ではWindows 10 Ver.1803が入っている。ここ数ヶ月放置していて、何か月分かのWindows Updateが未適用の状態だった。

Windows Updateで2019年7月の累積パッチがインストールされたので、適用のために再起動したところ、起動はするけど操作しようとするとブルースクリーン(INVALID PROCESS ATTACH ATTEMPT)になってしまう状態に陥った。

2台とも同じ症状だ。

ちょっと観察してみたところ、Vostro 2521本体のタッチパッドで操作しようとするとブルースクリーンになることが分かった。試しにUSBマウスを使うようにしたら、ブルースクリーンを回避できた。

タッチパッドは、Windows Update前には普通に使えていた。ブルースクリーンが起こるようになる前との違いは「Windows Updateをかけたか否か」だけ。

何というか、ここまで堂々とWindows Updateが疑わしく見えるシチュエーションに遭遇すると、一周回って感心してしまうものだ。

なおブルースクリーンについては、遅れてKB4346084(今月再リリースされた版)が自動適用されたら、発生しなくなった。2台とも同じ。

KB4346084は「Intel製CPUのマイクロコード更新する」という少々特殊なパッチなのだが……適用順でブルースクリーンなる、という情報は見つけられていない。

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

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

なおCSS、HTML、XMLはひとまず除外する。人工言語ではあるけれども「プログラミング言語」という括りに含められるか否かは議論が分かれる気がする。*1

よく使っている

AWK (Gawk)
単純なテキストレコードの処理はAWKで十分間に合う。今の時代、自作ツールをnawkやGNU awk単体で実装するのは苦行すぎて*2皆無なものの、シェルスクリプトMakefileAWKのコードを埋め込むなどして他のコマンドと組み合わせて使う機会は依然として多い。シェル上でワンライナーでテキスト処理する時にも重宝している。これはこれで十分AWKらしい使い方ではないだろうか?
C++
ちょくちょくお仕事で使うが、本職のC++使いではない。C++11やC++14は非常に便利で、better Cでも使う価値がある。C言語使いからすると、C++03時代よりも充実度が進んだ標準ライブラリ・ラムダ式・autoによる型推論など、便利で羨ましい限りだ*3。あと、Swift時代のクロスプラットフォームC++ライブラリ作者は、どうあがいてもARCから逃れられないので、C++11以降のスマートポインタは必須だ*4正規表現とスレッドも標準ライブラリに加わったので、あとはソケット(低水準ネットワークAPI)をサポートしてくれないだろうか。低水準の処理を行いつつも便利な機能で実装時間を短縮できる点は便利で、少なくともシステムプログラム言語としての利点だと思う。だけど機能多すぎ/複雑すぎなところはなんとかならないものか。強力な反面、使い手を選ぶ言語だ。
C言語
お仕事での主力言語。シンプルかつ低水準の世界が垣間見れるところが割と好き。とはいえ最近の他の言語と比較すると、シンプルすぎて安全機構が欠けていたり標準の便利機能が少なかったりするので、入門用の言語としては薦められない。にもかかわらず、プログラミング未経験者向けのC言語の本は毎年出版されている――謎だ。クロスプラットフォームなモジュール屋としては、今までC89を採用してきたものの、いい加減そろそろC99とかC11とか次世代の言語とか使いたい。でも古いVisual Studioもサポートしたいから、C99すら2023年~2024年ぐらいまで使えない。悲しいなぁ。
DOSバッチファイル
プログラミング言語に含まれるかどうか不明だが、含めてしまう。ちょっとした自動化や、複数ツールを組み合わせて使うときのラッパーとしてよく使う。コマンドプロンプトはシバン(shebang)に対応していないので、スクリプト言語で書いたツールを起動するラッパーとしても多用している。意外と色々なコマンドが用意されているので、単純にそれらを叩く分には十分だが――言語機能がショボいので、バッチファイルでifやforのような制御構文系コマンドが必要になってきたら、何かを決定的に間違えているのではないかと考え直すようにしている。
Kotlin
本格的にAndroidアプリ開発に関わるようになったのがGoogle I/O 2017直後の過渡期なので、JavaよりもKotlinでの経験値の方が多い。モダンな「強い静的型付け」の、割とええ感じの言語やね。ただ、Swift 3をつまみ食いして半年以上経ってからKotlinをつまみ食いしたこともあり、未だに両者の概念・機能が頭の中でごった煮になっている。それと、NDK絡みの作業が多いので、C++11/14・Java 7/8・Kotlinを行ったり来たり。泣けるぜ。Swiftもそうだが、「強い静的型付け」の言語は「開発環境込み」で高い生産性とコードの安全性を両立させる方向に進んでいる気がする。
make (Makefile)
プログラミング言語に含まれるかどうか不明だが、DSL扱いで……いやGNU Makeはそこそこプログラミング言語的か。GNU Make 4.0はさらにプログラミング言語的だな、特にGNU Guileなところが。GNU MakeとNMAKEが主力で、稀にNetBSD Make(pmake)を使うが、いずれも独自拡張アリアリだ。もう素のmakeではMakefileを書けない身体だ :)
Python
Python 2.xで実装した小ツール群をPython 3.xに移植した。Pythonではlazyなスタイルでのコーディングが許されず、整然とコードを記述する必要がある。その辺は、Perl 5やRubyとは随分と雰囲気が異なる。気になるのは、インデントが必須な言語仕様であるために、シェルスクリプトに埋め込んで使うのが苦痛な点だ。Pythonだけでコードを書く分には気にならないのだけど。
sed
プログラミング言語に含まれるかどうか不明だが、DSL扱いで*5。テキスト処理用。シェルスクリプトMakefileにて他のコマンドと組み合わせて使う。というか正規表現でのテキスト置換以外の機能を使った記憶が……あったな、dとiとpと=とブレースによるグループ化ぐらいだが。私のレベルではsedFizzBuzzを書けないので、sedで難しい処理を記述しないようにしている。
Windows PowerShell
時代はPowerShell Coreらしいが、現行のWindows 10でデフォルトで利用できるv5.1に留まったままである。スクリプト言語としてのPowerShellは、オブジェクト指向.NET Frameworkを叩けてダイナミックスコープでスクリプトブロック(という名の無名関数)と、無茶でピーキーで完全にプログラマ向けな代物だ。Microsoftもよくもこんなエライ代物を出したものだ。残念なことに、コマンドプロンプトの代替という観点では、外部ツールとの親和性が微妙にイマイチだ(特に文字コードとか)。でもPowerShell内で閉じている分には問題ないので、私の手元では「Windows専用のGUI付き小ツールを作るためのスクリプト言語」か「Excel COMとか叩く用のスクリプト言語」か「Windows Serverの管理スクリプトを書くためのスクリプト言語」扱いしている。ところで、いい加減『Windows PowerShell イン アクション』級の最新バージョン対応の言語解説書が出版されてくれないだろうか。
シェルスクリプト (/bin/sh)
プログラミング言語に含まれるかどうか不明だが……いや、私的にはシェルスクリプトは立派なプログラミング言語だ。基本的な用途は、バッチファイルと同じくちょっとした自動化や複数コマンドを組み合わせて使うときのラッパーだが、実現できる内容は遥かに多い。言語本体(?)がバッチファイルよりも高機能だし、Unixユーザランドはコマンドが充実している。その意味では、WindowsではMSYSよりもCygwinで――いやむしろWSL(Windows Subsystem for Linux)で環境構築すべきだろう。Cygwinでは、主要な処理をシェルスクリプトで記述しておき、bashからはシェルスクリプトを利用し、コマンドプロンプトではラッパーのバッチファイル経由でシェルスクリプトを叩く使い方をしている。ただWindows上では処理速度が妙に遅くなる点が不満だ。

あまり使っていない

Free Pascal
お試しで触っているのだが、微妙にDelphi/Free Pascal初心者(ただし他言語の経験者)向けの良い資料が少なくて難儀している。玉石混交なのだ。いっそのこと『OBJECT PASCAL HANDBOOK―マルチデバイス開発ツールDelphiのためのプログラミング言語完全ガイド』を買ってしまおうかしら。
Java
生まれて初めて触れたプログラミング言語その2。実のところ、職業プログラマとして本格的に使用することは一生ないと思っていた。Androidアプリ開発も、Kotlin採用後に本腰入れて関わるようになったので、Kotlinメインだ。だが、なぜかぬるい感じに時々Javaのコードを触っている。ただし、先にコレクションの操作方法が充実した他の言語を学んでからJavaを本格的に触るようになったので、Java 8以降のStream APIが使えないと身体が拒否反応を示す。少なくとも、構文の見た目こそ保守的なオブジェクト指向プログラミングをしているC++に似ているけど、中身はC++とは似ても似つかない代物だということは体感している。
Objective-C, Objective-C++
時代はSwiftだと言われているけど、どっこいObjective-CObjective-C++は生きている。というかSwiftのコードにC++で書かれたライブラリを直接組み込むことができない以上、両者を繋げるグルー言語として生き残ることになるよね。最近流行の言語と比べると良くも悪くも80年代的だが、アプリケーションプログラミング用としてはC言語よりマシだし、C++ほど複雑怪奇*6ではない。そしてC言語C++で書かれた既存のライブラリをそのまま使える。Objective-Cのハイブリッドな所は好きだが、Objective-C++はハイブリッドすぎて微妙。C++のクラスとObjective-Cのクラスを、C++ラムダ式Objective-Cのブロック構文を同時に使うのは大変だ。便利ではあるんだけどね。
Perl 5
時々、やむをえない事情で触ることがある。だが基本的によく分からない。何というか、あの記号の羅列っぽさに中々慣れないというか、自分は余りに自由度が高すぎる言語は苦手だと気づいたというか。(言語仕様に慣れているなら)半ば使い捨てなテキストフィルタとかをさっと書くには、悪くない言語だと思うのだが。
QML
宣伝文句のとおり、QMLはGUIの記述に非常に向いている。それも、単に標準のUI部品(エレメント)を使うだけでなく、少し改造して使うとか、オリジナルのUI部品を作って使うとか、それらを別のアプリケーションに使いまわすとか、そういう時に威力を発揮する。あと、プロパティバインディングやレイアウトのアンカー指定など、画面サイズの変更に追随するUIを作りやすい機能も揃っている。JavaScriptでちょっとした処理も記述できる――とはいえ、やりすぎるとパフォーマンスの罠が……。少なくとも、JavaScriptでゴリゴリコードを書くのはQML的ではない。QMLは宣言的に、シンプルに書くものだ。力技でロジックでゴリ押しすると、色々と罠に嵌る言語だ。
Ruby
自作ツール実装にて、AWKの代替言語の最有力候補だった(でも最近はPythonが多いんだな、これが)。テキスト処理でも割と重宝するが、バイナリデータへの変換が絡んでくるとAWKよりもRubyを使った方が効果的だ*7。そろそろirbを電卓代わりに使うスタイルも板に付いてきた気がする。to_s(16)やto_s(2)で基数変換して表示できるところが好き。
SQL
生まれて初めて触れたプログラミング言語その3ぐらいに位置する。組み込みの人なのでSQLとは無縁だと思っていたが、まさかTransact-SQLを少しだけ触ることになるとは。
Swift
コンパイラによる強力な型推論と型安全性のチェック」がお仕事用のメジャーな言語にまで降りてきたという点で、Swiftは静的型付け言語界のJavaScript*8だと思っている。でもユーザ数的には、Kotlinが「静的型付け言語界のJavaScript」ポジションなのかもしれない。割と好感が持てる言語だ。そろそろ互換性のない言語仕様の変更が発生し難くなってきた感じかなあ。
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#には、なかなか慣れない。
CASL II
生まれて初めて触れたプログラミング言語その1。何だかんだで、後でCプログラマになってからも低水準での思考ツールとして微妙に役に立っている。まあ考えるための言語であって実用言語ではない。仮に実用的な処理系*9があったとしても余りに命令がシンプル過ぎて悶絶するなあ、なんてFizzBuzzしてみて思った。
flex (lex)
プログラミング言語に含まれるかどうか不明だが、DSL扱いで。字句解析用のツールという印象が強かったのだが、よく考えてみたら、flexは「sed(1)のよくある使い方」と同様に「正規表現でパターンマッチング --> 何らかのアクション」という内容を記述するためのツールだった。ただ単に、「何らかのアクション」をC言語で書けることと、flex自体ではパターンマッチングをせずに「パターンマッチングするC言語のコード」を生成することが少々風変わりなだけ。grep(1)やsed(1)その他で小ツールを実装して運用しつつ、性能が求められたらflexで専用ツール化する――とか考えたけど、普通にgrep(1)やsed(1)を使う方が高速だった。
Go
寡作ながらもいくつか小ツールを書いてみたが、標準ライブラリが充実しているコンパイラ型言語っていいっすね。C言語に比べればC++の標準ライブラリも充実しているが、どちらかといえばプリミティブな機能が中心だ。PythonRubyばりの標準ライブラリを抱えているGoには及ばない。その辺は、やはりCプログラマ(特にCでフィルタやデーモンの類を書く層)には受けそうな言語だと思う。並列処理周り(goroutines)とかARM対応とかが気になる。ソフトリアルタイム限定だが「組み込みLinux + Goで書いたデーモン」とかどうだろう? ただメモリを食うらしいという噂がどうなったか気になる――64bit環境では解消されるという話だったようだが、32bit環境でも解消されるようになったのだろうか? 組み込みでは現時点では逆立ちしたって64bit CPUはありえないからなあ、スマホタブレット以外では。
Io
プロトタイプベースである点を除けば、何となくSmalltalk的であるような――公式ドキュメントらしきIo Programming Guideでも影響を受けた言語として真っ先にSmalltalkが挙げられているから、あながち思い違いでもないだろう。今更ながら『7つの言語 7つの世界』のIoの章を読み終えたので、ちょっとしたコード片を書いているが……Windows版のバイナリが古いためか、リファレンス通りなのに動作しないコードに直面している。
JavaScript(クライアントサイド)
クライアントサイドJavaScriptのコードを書いたこともあったけど、スキル的にはもう何もかも賞味期限切れだよなあ。ECMAScript 2015以降を使うなら、それ以前とはコードのスタイルから変えないと不味そう。
JavaScript(サーバサイド?)
Node.jsやPhantomJSが出てきた頃は、クライアントサイド以外でもJavaScriptが使われる文化が広まる可能性があるのか気になったものだが……結局どうなっているの? 何か処理系もNode.jsに収束してきた印象が。
Lua
Wiresharkのパケット解析スクリプトを書いてから数年。今度はC言語で書かれたUnixデーモンの設定ファイル用に組み込むことに。これはこれで「職業柄」の範疇、なのだろうか? Windowsのことを考えなければ、自前でライブラリをビルドしてアプリに組み込むのは結構簡単だった。
Scheme
GaucheWindowsネイティブ環境用バイナリは実験版だが、私が触る分には何の支障もない*10ことに気づいて久しい今日この頃。『Scheme手習い』と『Scheme修行』を購入したので、とりあえずCommon LispではなくGaucheScheme)の勉強をする方向に転換しようか検討*11しているうちに何年たったのやら。Gaucheはフィルタ・ライクな小ツールの実装用としても良い感じだ。しかし最も多い利用方法はREPLを電卓代わりにすることだ*12。うーん、作業環境がmacOSLinuxに移ったなら、大手を振ってGaucheでフィルタを書くのだが。
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よりもポイントが高い*13。個人的には、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用のどの処理系も日本語の置換に何かしらの問題を抱えていたので泣く泣く諦めた。思うところがあって改めて少し触ってみたが――なるほど、確かに中毒性のある言語*14だ。
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で舐めれなかったりして、結局は必要に駆られて使用することに。明快に記述できる文法は評価に値するが、スクリプト言語としては少々冗長だ。配列は自動拡張しないし、組み込み関数はプリミティブ気味だし、冗長気味な文法との合わせ技でコードがさらに冗長になっていく……。文法や言語仕様の詳細なドキュメントが見つからないのだが、どこにあるのだろうか?*15
秀丸マクロ
7年ほど秀丸エディタを使っていたが、マクロを書く機会はなかった。一念発起してFizzBuzzしてみて感じたのは、最近の便利な言語に慣れた身としては色々とモヤモヤ感がある言語仕様だということ(歴史的経緯的に仕方ないのだが)。とはいえちょっとした拡張ツール的なものを手軽に作れそうではあった。

*1:HTML5 + CSS」の組み合わせなら、チューリング完全の疑惑があったり、JavaScript使わずにCSSでWebチャットを作った猛者がいたりと、色々と怪しいのだけど。

*2:「独立性の高い単体のツールを実装する」という視点では、現代ではAWKよりも便利な言語が山ほどある。

*3:しかし標準ライブラリの充実度をJavaC#.NET Framework含む)と比較したり、型推論まわりをKotlinやSwiftと比較してはいけない。以前よりも随分と便利になったのだけど、だけど、隣の芝生を見てしまうと、うーん。

*4:SwiftではC++の機能を直接呼び出すことができないので、Objective-Cでラッピングした上で利用することになる(インタフェースはObjective-Cで、内部実装はObjective-C++)。この時、Objective-Cクラスのインスタンス変数として「C++クラスのインスタンスを保持するスマートポインタ」を持つ構成にしておくと、Objective-Cクラスのインスタンスがdeallocされる時に、スマートポインタ経由でC++クラスのインスタンスもdeleteされるのである。

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

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

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

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

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

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

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

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

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

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

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

コマンドプロンプトからUSB記憶装置を取り外そうとして失敗し続けている記録

WindowsUSBメモリなどを取り外す際にタスクトレイから行う「ハードウェアの安全な取り外し」の操作をコマンドプロンプトから実現しようとして、依然として失敗し続けている。

備忘録として、時系列順に記録を残しておこうと思う。何か進展があったら追記するつもりだ。

おま環の問題かもしれないので、もう少し具体的に背景を書く:

  1. Windows Storage Server 2016 Workgroupで運用しているNASがある。
  2. このNASには、NTFSでフォーマットしたUSB-HDDが取り付けてある。
  3. USB-HDDは、Windowsバックアップツールを利用した定例バックアップ先として使用している。
  4. USB-HDDは、定例バックアップの時だけ接続している。バックアップ終了後には、取り外し可能な状態にした上で、USB-HDD本体の電源をOFFにしている。
  5. 諸々の事情で、定例バックアップはWbadmin.exeを叩くパッチファイルをタスクスケジューラで実行することで実現している。
  6. 定例バックアップ完了後に、毎回NASにログオンして「ハードウェアの安全な取り外し」の操作をするのが面倒である。なので同様の操作をパッチファイルから実行することで自動化したい。
  7. NASは業務用として運用しているので、Windows Storage Server 2016の標準の機能か、もしくはMicrosoftが公開しているツールなどで実現したい。サードパーティのツールは導入したくない。

以上の理由より、コマンドプロンプト(正確にはパッチファイル)から標準/準標準の機能で「ハードウェアの安全な取り外し」と同等の処理を実現しようと試みているのだが、未だに成功する気配がない。

今のところ得られている結果は「そもそもアンマウントすらできない」か「運用上問題となる副作用が発生する」のどちらかである。

1. PowerShell : Shell.Application 経由で Eject

PowerShellを使ってCD-ROMドライをイジェクトするネタがあるのだが、同じ方法でUSBデバイスもいけるという情報を得たので、試してみた。

# 実行例 - F: を取り外す場合
(New-Object -ComObject Shell.Application).Namespace(17).ParseName('F:').InvokeVerb('Eject')

結果:アンマウントすらされない。

2. Windows Sysinternals : sync のオプション -e を使う

Windows Sysinternalsのsync.exe/sync64.exeのオプション-eでイジェクトできるとの情報を得たので、試してみた。

:: 実行例 - F: を取り外す場合
sync64.exe -nobanner -e F

結果:アンマウントすらされない。

3. 標準コマンド : mountvol を使う

標準コマンドmountvolのヘルプを見たところ、オプション/pにそれっぽい内容の記述があったので、試してみた。

:: 実行例 - F: を取り外す場合
mountvol F: /p

結果:ディスクがアンマウントされ、オフラインになる。その後にタスクトレイでの取り外し操作は必要だが、既にオフライン状態なので、直ぐに取り外し可能状態になる。

問題点:この後ディスクの電源をONにした時、ドライブレターの割り当てが外れている。例えば「mountvol F: /p」でFドライブのUSB-HDDを取り外した場合、そのUSB-HDDを再度接続した時、ドライブレターが全く割り当てられない(なのでエクスプローラーの「デバイスとドライブ」からはUSB-HDDにアクセスできない)。

4. PowerShell : Set-Disk でオフラインにする

PowerShellのSet-Diskでゴニョゴニョできる、という情報を得たので、試してみた。

# 実行例 - F: を取り外す場合
Get-Disk | ?{ $_.FriendlyName -eq 'I-O DATA HDJA-UT' } | Set-Disk -IsOffline:$true

結果:ディスクがアンマウントされ、オフラインになる。その後にタスクトレイでの取り外し操作は必要だが、既にオフライン状態なので、直ぐに取り外し可能状態になる。

問題点:この後ディスクの電源をONにした時、ディスクがオフラインのままになる。なのでSet-Diskを使うなどして明示的にオンライン状態にしないとUSB-HDDが使えない。

書籍購入:『Kotlinプログラミング』

あとで書く。

Kotlinプログラミング

Kotlinプログラミング