後ろの方に追記をいくつか書いているのでそちらも是非参照ください

今日さくらインターネット研究所の雑談タイムで、viの終了時には :wq や :q! とかで終了するよりも ZZ で終了すべき、という話題が出た。

ここで簡単に整理しておくと、

:wq はファイルを上書き保存して終了

はファイルを上書き保存して終了 :q は上書きせずに終了

は上書きせずに終了 ZZ はファイルに変更があれば保存して終了、なければ上書きせずに終了

というコマンドである。

最初は ZZ 便利だよなぁと思っていたけど、確か過去に ZZ だとやりにくいところがあって使うのをやめた記憶があった。それで色々話をしていると、やっぱり ZZ を使った方が良いケースが思いつかなかった。 そこで、 ZZ いらんでしょ、などと発言したりしていたのだった。

といのうも、僕のviの終了するパターンとしては、

まず :q を押す 変更がなければそのまま終了、変更があれば変更があるよとwarningが出て終了できない warningが出れば何らかの変更をしているはずなので確認する 変更があれば :wq 、変更がやはり不要であれば :q! で終了する

である。

つまり、このケースの場合 ZZ のように自動でやってしまうと、自分の想定していない変更が保存されてしまう可能性があるし、なにも考えずに :q を押してから変更の有無の判断を仰ぐのと比べて、 ZZ の前に一度更新されているか確認する C-g コマンドを打ってから ZZ する必要があるので、1遷移だけ手間が増えるじゃん、しかも :q だと1遷移で終了できる場合もあるのでコスト低くていいじゃん、と考えたのであった。

しかし、これをちゃんと考えてみると、実はそんなことないことがわかってきた。

そこで、遷移数の数によるコストを考えてみた。

vi終了時の遷移コストと人間の条件分岐コスト

まずファイルに変更があるかないか、という頻度は同じ割合で起きるものとする。また、変更がない場合には当然タイムスタンプを上書きしない、というのも前提とする。簡単のため、 :wq などで終了する方法を松本手法、 ZZ で終了する方法を鷲北手法とする。

松本手法だと、 :q で変更がない場合、1遷移で保存が完了する。一方、変更があった場合は、 :q でwarningが出た後に、変更が正しければ :wq で保存、変更が不要であれば :q! などで終了する。このため、変更があった場合は :q と( :wq または :q! )で、全体の遷移数は2であり、合計の遷移数は3遷移となり、かつ、変更が必要かどうかという条件分岐の後に :wq または :q! かという正しいコマンドを選択するという条件分岐処理が発生する。

鷲北手法の場合、まずは何も考えずに C-g でファイルの変更状態を確認し、変更がなければ ZZ で終了、変更があればその変更内容を確認した後、変更が正しければ ZZ で終了、変更が不要であっても ZZ で終了する(追記: ここが本来はuなどで消してからZZするか、:q!する条件分岐が結局必要になり、論理がおかしくなるので最後に追記した。なので、ここからまとめまでは雰囲気をお楽しみください)。

このため、変更があった場合もなかった場合もどちらも2遷移かかるため合計4遷移となり、松本手法と比べて合計1遷移だけコストがかかるように思える。

ここまでで、一見松本手法の方が効率が良いと思ったのであったが、もう少し踏み込んでみるとそうではないことがわかってきた。

ここで発生する条件分岐のコストは、CPUのパイプライン処理で考えると基本的には条件分岐判定の間処理をブロックしてしまうこと、さらに、条件分岐判定は人間が行うため、単純な遷移コストと比べて非常にコストが高いと考えられる。一方で、鷲北手法は変更があろうとなかろうと、条件分岐がなく、常に C-g してから ZZ という遷移を行うため、条件分岐のブロックが生じない。

つまり、変更がないのにファイルのタイムスタンプを変更したり、ファイルの保存の失敗をしたりしない、という制約においては、松本手法は必ず :wq か :q! の条件分岐が生じる。そして、その分岐予測コストが1遷移以下であれば、松本手法の方が低コストと言えるが、人間が条件分岐を判定する速度はコンピュータの処理速度と比べて非常に遅いと考えられるため、1遷移で達成できるとは到底考えられない。

人間に有利に見積もっても、5遷移程度のコストはかかるだろうし、例えばファイル内容の変更がないのにタイムスタンプを変更したりしてしまったらサーバが爆発する場合には、条件分岐には慎重になるため、100遷移コストぐらいかかるだろうし、サーバを爆発されないように運用手順書を作るのであれば、 :wq か :q! であるかを入力後、別の作業者に確認してもらう、みたいな手順を追加することも必須だろう。そうなると、更に条件分岐のコストが高くなってしまい、松本手法がますます高コストな手順になってしまう。

そんな状況で鷲北さんがやってきて、「 ZZ すればいいじゃん」といえば、サーバが爆発する問題も解決するし、作業者はよろこんで ZZ の手順を導入し、作業手順から確認作業を削除するだろう。

まとめ

ということで、自分の使い方を前提に考えると一見コストが有利なように思える作業も、研究者である以上、前提条件や制約を整理した上で適切にコストを定量評価すれば、 :wq か :q! かを判定する条件分岐が1遷移コストよりも大きければ、 ZZ の方が終了する際には効率的であることがわかった。また、条件分岐時に間違えられないという制約があると、より条件分岐のコストは高くなるため、人間が作業する以上 ZZ がさらに効率的になることがわかった。(追記: 結局よくわからなかったので下記の追記にまとめた)

ということで、viを終了するのは C-g してから ZZ が効率的である！鷲北さん、色々適当なことを言ってすみません！（鷲北さんとはさくらインターネット研究所の所長です） (追記: 結局効率面でもそんなことなかったので下記の追記にまとめた)

追記: 2019/06/25 23:40

とここまで書いておきながら、コメント等をみていると考慮できていないことがいくつかあって、

ZZ の場合で、変更があったけど保存したくない場合には、 ZZ で終了しようとすると変更を削除してから ZZ する必要があるのでそのコストをどう考えるか

の場合で、変更があったけど保存したくない場合には、 で終了しようとすると変更を削除してから する必要があるのでそのコストをどう考えるか そもそも ZZ でも変更があっても適用したくない場合は :q! を使えば良い、となると同様に条件分岐が発生して単純な遷移コストの差になる

でも変更があっても適用したくない場合は を使えば良い、となると同様に条件分岐が発生して単純な遷移コストの差になる ZZ であっても :q! で終了する条件分岐を考慮するとなると、今回の松本手法と鷲北手法の差は :q で終了とともに変更確認をするか、 C-g で変更確認するステップをいれてから終了するかという差になり、その点で、 :q は変更がない場合に1遷移省略できるので、全体として松本手法が有利になる

であっても で終了する条件分岐を考慮するとなると、今回の松本手法と鷲北手法の差は で終了とともに変更確認をするか、 で変更確認するステップをいれてから終了するかという差になり、その点で、 は変更がない場合に1遷移省略できるので、全体として松本手法が有利になる ただし、キーストロークの数をいれると ZZ は1遷移の効率が良い

という話が考えられる。

このことから、このタイトルの効率面を考え、かつ、キーストローク数を一旦置いといて遷移数的な話でいくと松本手法がコスト的に有利になる。が、キーストロークとかもいれると少し計算がややこしくなるので、誰かアンサーブログを書い(ry

さらに追記: 2019/06/26 00:27

つまり今回のエントリの話題を整理すると、

:q によって終了と同時に変更があれば終了が停止されてから、変更の適用を :wq or :q! で決定し終了するやり方（ :q によって変更なし終了の条件は判定が終わっているから） C-g やstatuslineなどで変更があるかを確認してから、変更がない場合と変更があって適用したい場合は ZZ 、変更を破棄したい場合は :q! で終了するやり方

のどちらの思考や覚えたコマンドの癖が自分にあっているかという感じだろうか。

僕は終了時に編集の状態を意識的に確認するよりは、なにも考えず :q で一旦終了を試みてから止まったらはじめてそこで変更内容について考えて、保存して終了か変更を破棄して終了するコマンドかをそれぞれ叩く、という思考がどうもあっているように思える。

実際にこの話をしていた鷲北さんの色々な見解も興味深いですので是非ご覧ください。

north.thco.mp