VimでDvorakしよう2013年02月13日 00:02

この記事は Vim advent calendar の75日目です。74日目は Mi_Sawaさん の template/snippet plugin for Battle Programmer でした。

標準的には25日目までなはずですから、なんと3倍!クリスマスっていつでしたっけ。明日はバレンタインデーですよ。えっ……中止?

あなたはQWERTY派?それともDvorak派?

突然ですが、キーボードレイアウトの時間です。

みなさまおなじみQWERTY

+----+----+----+----+----+----+----+----+----+----+----+----+
| !  | @  | #  | $  | %  | ^  | &  | *  | (  | )  | _  | +  |
|  1 |  2 |  3 |  4 |  5 |  6 |  7 |  8 |  9 |  0 |  - |  = |
++---++---++---++---++---++---++---++---++---++---++---++---++
 |    |    |    |    |    |    |    |    |    |    | {  | }  |
 |  Q |  W |  E |  R |  T |  Y |  U |  I |  O |  P |  [ |  ] |
 ++---++---++---++---++---++---++---++---++---++---++---++---+
  |    |    |    |    |    |    |    |    |    | :  | "  |
  |  A |  S |  D |  F |  G |  H |  J |  K |  L |  ; |  ' |
  ++---++---++---++---++---++---++---++---++---++---++---+
   |    |    |    |    |    |    |    | <  | >  | ?  |
   |  Z |  X |  C |  V |  B |  N |  M |  , |  . |  / |
   +----+----+----+----+----+----+----+----+----+----+

Dvorak

+----+----+----+----+----+----+----+----+----+----+----+----+
| !  | @  | #  | $  | %  | ^  | &  | *  | (  | )  | {  | }  |
|  1 |  2 |  3 |  4 |  5 |  6 |  7 |  8 |  9 |  0 |  [ |  ] |
++---++---++---++---++---++---++---++---++---++---++---++---++
 | "  | <  | >  |    |    |    |    |    |    |    | ?  | +  |
 |  ' |  , |  . |  P |  Y |  F |  G |  C |  R |  L |  / |  = |
 ++---++---++---++---++---++---++---++---++---++---++---++---+
  |    |    |    |    |    |    |    |    |    |    | _  |
  |  A |  O |  E |  U |  I |  D |  H |  T |  N |  S |  - |
  ++---++---++---++---++---++---++---++---++---++---++---+
   | :  |    |    |    |    |    |    |    |    |    |
   |  ; |  Q |  J |  K |  X |  B |  M |  W |  V |  Z |
   +----+----+----+----+----+----+----+----+----+----+

私は、ながらくDvorakつかっています。Dvorak布教するつもりはあまりないので理由は延べませんが、使い始めてそろそろ11年経ったことに気づきました。うぎゃぁー

……えっと、残念ながらハードウェアでDvorakなキーボードは所持しておりませんが、ソフトウェアでDvorak設定にする派です。

プライベートで使ってる端末はすべてDebianのインストーラで真っ先にDvorakを選択しますし、 「いかんともしがたい理由により」Windowsな端末の場合でも、定番の「窓使いの憂鬱」やその後継の「のどか」を入れて、さくっと設定します。 場合によってはレジストリでもいいかもしれませんね!

大抵の場合は、キーボードレイアウトは、ソフトウェアレベルで比較的自由に設定できます。自由すばらしい。

私が本当にほしいのはたぶん自由ではなく「私に合った不自由」だと思うのですが、そんな意味不明な主張はさておきまして、やっとVimの話です。

DvorakでVim

Dvorak派のひとでも、Vimを使う場合、(特にノーマルモードの)コマンドのキーだけはQWERTYにしたい、という話をたびたび見かけます。 特に、QWERTY なら右手ホームポジション付近で横に並んでる HJKL が Dvorak では散り散りになってしまうののを嫌う向きは多いみたいです。

ですが、Dvorakのそのままでもいいじゃん、というひともいます

私はまさに、この考え方に同意します。HJKLを使う頻度ってそれほど高くない気はするものの

jk が隣り同士でしょ。hl は離れてるけど、きちんと左と右側に位置してるでしょ。

しゅごい!!

しゅごいのです!!!

補足しますと、垂直移動の jk が左手、水平移動の hl が右手に分かれているのが、なんともすばらしい。

ほとんど排反といっていい操作が同じ側の手に、独立であってほしい操作が違う側の手に、それぞれきれいに割り当てられています。 片手での操作の難易度が上がるというデメリットはあるものの、普通に使う分には Dvorak の方が効率的な操作が期待できます。

実に合理的です。Dvorak配列でVim、こわくないです

+----+----+----+----+----+----+----+----+----+----+----+----+
|    |    |    |    |    |    |    |    |    |    |    |    |
|    |    |    |    |    |    |    |    |    |    |    |    |
++---++---++---++---++---++---++---++---++---++---++---++---++
 |    |    |    |    |    |    |    |    |    |    |    |    |
 |    |    |    |    |    |    |    |    |    |  L |    |    |
 ++---++---++---++---++---++---++---++---++---++---++---++---+
  |    |    |    |    |    |    |    |    |    |    |    |
  |    |    |    |    |    |    |  H |    |    |    |    |
  ++---++---++---++---++---++---++---++---++---++---++---+
   |    |    |    |    |    |    |    |    |    |    |
   |    |    |  J |  K |    |    |    |    |    |    |
   +----+----+----+----+----+----+----+----+----+----+

……まあ、そういうことを考えてきめたというわけではないのですが、私はDvorakのままVimをカラダで覚えてしまっています。

立場をまとめてみます。

Vimつかってるよ派 (世界全体)
|
+-- QWERTYだよ派 (多数、保守)
|
+-+ Dvorakだよ派 (少数、革新)
  |
  +-- ノーマルモードとかのコマンドはQWERTYっぽいのがいいよ派 (穏健)
  |
  +-- Dvorakそのままでつかうよ派 (原理主義)  ← 私はココ

長くなりましたが、ここまで前置きです。

QWERTYでVim ……というディストピア(私限定)

ある日突然、QWERTYなキーボードで仕事することになったら……キーボードレイアウトの設定も変えられなかったら。 あんまり考えたくない事態です。でも、ありえないとは言い切れません。

もっとも、エディタはVim以外ありえません。ここはいいですね。先の図にも書いたとおり、Vimは世界全体です。

つまり、普段 DvorakのままVimを使っている私が、QWERTYでVimを使うことになったら何が起きるのか考えてみます。

たとえば、カーソル行とその一つ下の行を繋げたいとしましょう。もちろん J です。

Dvorakでは、左手中指下段です(私は人差し指で拾いますが)。アタマより先にカラダが動いてこのキーを押下!

結果……行末まで消えてしまいました。

そう、QWERTYキーボードでこの位置には C があります。

私はただ、次の行を繋げたかっただけなのに。行末まで消えてしまい、あまつさえ挿入モードに切り替わるだなんて。

こんなの絶対おかしいよ!

もうひとつ考えてみましょう。こんどは、カーソル行に新しく空行をつくりたいとしましょう。もちろん O です。

Dvorakでは、左手薬指ホームポジション!もちろん反射的に押下します。

結果……カーソル行の内容が消えてしまいました。

そう、QWERTYキーボードでこの位置には S があります。

たしかに空行はできましたが……私はあたらしい空行がほしかったのに。カーソル行の内容が犠牲になるだなんて。

こんなのってないよ!あんまりだよ!!!ひどすぎるよ……

たった2つだけの例ですが、恐ろしさは伝わりましたでしょうか。でもこれは氷山のほんの一角に過ぎません。

  • 左に動かそうとしたカーソルが下にいた。何を言ってるのかわからねーと思うが(ry
  • 行頭に挿入開始しようとおもったら最下行にいた。何を言ってのるかわか(ry
  • コロンを打ってみたつもりが反応がないのでもう一度打ったら、上書き保存されてVimが終了した。何を(ry

こんな環境に置かれ続けたら、私の魂はあっというまに穢れていくことでしょう。

VimでDvorak

取り越し苦労の最中、ふと気づいてしまいました。端末のキーボードレイアウトを設定できなくても、.vimrcで設定すれば、Vimの中でだけはDvorakが実現できるんじゃないか、と。 シェルには戻らずすべて :! とか徹底すれば、Vimの外に出ずに済むかもしれません……完璧!?

.vimrc は空に近いほどよいという私の好みには合いませんが、穢れきった私の魂が呪いを生み出す存在と成り果ててしまうよりはマシです。

Vimおねえちゃん、Dvorakを使い始める前の、ばかな私を、たすけてあげてくれないかな。

その結果、魂を対価に得られたものがこれです。

no - [
no! - [
no = ]
no! = ]
no q '
no! q '
no w ,
no! w ,
no e .
no! e .
no r p
no! r p
no t y
no! t y
no y f
no! y f
no u g
no! u g
no i c
no! i c
no o r
no! o r
no p l
no! p l
no [ /
no! [ /
no ] =
no! ] =
no a a
no! a a
no s o
no! s o
no d e
no! d e
no f u
no! f u
no g i
no! g i
no h d
no! h d
no j h
no! j h
no k t
no! k t
no l n
no! l n
no ; s
no! ; s
no ' -
no! ' -
no z ;
no! z ;
no x q
no! x q
no c j
no! c j
no v k
no! v k
no b x
no! b x
no n b
no! n b
no m m
no! m m
no , w
no! , w
no . v
no! . v
no / z
no! / z
no _ {
no! _ {
no + }
no! + }
no Q "
no! Q "
no W <
no! W <
no E >
no! E >
no R P
no! R P
no T Y
no! T Y
no Y F
no! Y F
no U G
no! U G
no I C
no! I C
no O R
no! O R
no P L
no! P L
no { ?
no! { ?
no } +
no! } +
no A A
no! A A
no S O
no! S O
no D E
no! D E
no F U
no! F U
no G I
no! G I
no H D
no! H D
no J H
no! J H
no K T
no! K T
no L N
no! L N
no : S
no! : S
no " _
no! " _
no Z :
no! Z :
no X Q
no! X Q
no C J
no! C J
no V K
no! V K
no B X
no! B X
no N B
no! N B
no M M
no! M M
no < W
no! < W
no > V
no! > V
no ? Z
no! ? Z

これをvimrcに貼り付けるなりsourceするなりで設定が効きます。

ちなみに こういうスクリプト で生成しました。

実際試してみて、ある程度Dvorakが実現できていることを確認しました。

……が、残念ながらCtrlのときはQWERTYのままです。

Ctrlのときの設定も少し挑戦してみましたが、ターミナルに奪われる場合とか、 Vimの外のことをやっぱり考えないといけない感じで、非常時に頼るわけにはいかない完成度です。今後の課題としましょう。

なお、Dvorakなキーボードレイアウトのときにこの設定をすると、大変なことになります(なりました)。注意しましょう。

明日は @tyruさん です。こんどこそ「ばよえ〜ん」ですね!おめでとうございます。

クリスマスを前にしてBFを先に作られてしまった話2012年11月11日 23:11

思うに、多少の欠点があっても目をつぶることって、ときにすごく大事なんだなって思います。 なんだそりゃって話なのですが、作ったBFの紹介で先を越されてしまって、大変くやしい思いをしてます。 この記事を読む大部分であろう異性愛者の男性の方々におかれましては、そんなにホイホイとBFをつくってしまっていいのか、あるいは果てしない苦労の先に手にいれるのがBFでいいのか、ちょっと落ち着いて考えるべきだと思います。

……ええと、おわかりだと思いますが、BFはもちろんbrainf*ckのことです。彼氏などいない!

キーボードマクロでbrainf*ckデバッガつくったよ

Vimで brainf*ck を作る試みは過去に thincaさんのものや mattnさんのものなど、いくつかあるようです。

しかし未だにVimScriptのよくわからない私がもっとも衝撃を受けたのは、つい先日 匿名の方がマクロで作った恐ろしいものです。

うわーん、今年の Vim Advent Calendar に使おうと思ってたネタだったのに!

……というのはさておき、 発表者は私ではないかと想像された方が何人かいらっしゃったようですが、残念ながら違います。 私はあんなに頭おかしくありませんし、マクロ漁船に乗船できる自信あんまりありません(ぉ

というわけで、もはや保存しておく意義が特にない「キーボードマクロで実装するbrainf*ckに対する私なりの解」をさっさと紹介してしまうことにします。

いろいろ欠点あるのですが「これはデバッガだ!」と言い張ればそれっぽく見えないこともない気がしているのでそのように主張しつつ紹介することにします。

マクロ漁船の匿名の方の発表と重なってしまう部分もあるかと思いますがご容赦ください。

特徴

  • @b でステップ実行できます。10@b など、ステップ数を指定して実行することもできます。
  • PCとポインタとメモリ空間をすべて可視化します。編集も可能です。
  • 名前付きのレジスタを2つだけ使います。(@b@x)

実行準備

まずは、お手元のVimを起動して、[CTRL-W]n を 2回ほど実行しましょう。あと、おまじないで、ステータスラインを2〜4行ほど上に持ちあげておきます(やらないとうまく動かないことがあります)。

ここまでできたら、一番上のウィンドウにこれを書いてください。

  • yl^Wk"xY^Wj@=(@0=='>'?'^V^Wk+^V^Wj':@0=='<'?'^V^Wk-^V^Wj':@0=='+'?'^V^Wk^V^A^V^Wj':@0=='-'?'^V^Wk^V^X^V^Wj':@0=='.'?'^V^Wj"=nr2char(@x)^V^Mp^V^Wk':@0=='['?@x?'':'%':@0==']'?@x?'%':'':'').' '

なお、ここに限り「^V」のように書いてあるのは[CTRL-V]と同じ意味です。

書けましたら、"bdd します。あと 30000O0[Esc] です。

最後に、真ん中のウィンドウにbrainf*ckソースコードを貼り付けます。

…と、ここまでの操作をきちんとこなすのは結構骨なので、お手軽実行ファイルを用意しておきました。

bfdebug.vi をダウンロードして、vim -s bfdebug.vi hello.bf のようにしてVimを起動するか、起動後のVimで:so! bfdebug.viしましょう。

これで準備完了です。

実行

プログラムの実行開始したい部分、つまり先頭行1カラム目にカーソルをもってきて、1000@b などとしてみましょう。 私の手元の環境で実行してみた様子を、以下に貼っておきます。

hello.bf

無事Hello, world!が出力されましたね。

たった218バイトでbrainf*ckデバッガができちゃうなんて、Vimはほんとにすごい!

え、デバッグしてない、ですか?すいませんbrainf*ck書いたことないんです……

備考

制限がいろいろあるので、gdgdと書き並べてみます。

  • ステップ実行しかできません。一度に走らせるには9999ステップ実行とかで代用してください。
  • コメントに使える文字が限られているかもしれません。変な文字(特に括弧やバックスラッシュ)は使わないでください。試してないだけなので、意外と大丈夫かもしれませんが……
  • メモリがバイト列でないため、オーバーフロー時および負になったときの挙動に依存したbrainf*ckプログラムは正しく動作しません。
  • メモリ空間は、ひとまず30000です。増やすことはできますが、マクロ実行前に決めておく必要があります。
  • 負のアドレスにアクセスできません。
  • 出力命令「.」は標準出力ではなく、バッファへ書き出します。マルチバイト文字はおそらく正しく出力できません。
  • 入力命令「,」は未実装です。単に無視されます。
  • ブレークポイント(独自拡張)を実装するつもりだったのですが、たかだか十数バイトを書き加える気力が捻出できないため、ありません。
  • Vimでしか動きません。nvi非対応です。

ともあれ、キーボードマクロの認知度が上がって世の中に受け入れられつつあるようで、この傾向が今後も維持されることを願ってやみません。

あと、Vim Advent Calendar 2011電子書籍、無事公開されましたようで、おめでとうございます。 前回は変なこと書いてすみませんでした。

Vimおねえさんといっしょ!みんなで数えてみよう!2012年09月30日 03:43

Vim Advent Calendarがなかなか電子書籍化しないのは、もしかして私がanimated gifなんか貼ったせいなんじゃないかと微かに罪悪感を感じる今日この頃の plaster です。 ごぶさたでした。

さて、 ちょっと前に話題になった「『フカシギの数え方』 おねえさんといっしょ! みんなで数えてみよう!」というすばらしい動画があります。

おねえさんが命をかけて組み合わせ爆発のすごさをつたえてくれる壮絶なストーリーなのですが、 おねえさんのコンピュータを作ってみた方までいらっしゃって、ほんとにみなさんすごいですね。

ところで、この記事を読んでいて、ひとつ気づいたことがありました。

小さいなサイズなら単純な深さ優先検索(DFS)で解けます

なるほど……たしかに解けますね。Vimで

レギュレーション

  • たとえば 2×2 の道があったら
    #####
    #   #
    #   #
    #  G#
    #####
    0
    こういうテキストで表すことにします。ゴールは「G」にします。
    • 3×3 の場合は
      ######
      #    #
      #    #
      #    #
      #   G#
      ######
      0
      となります。
  • 空白は歩ける場所で、「#」が通れない、壁です。空白のうち、いちばん左上にあるところがスタートです。
    • 見た目はちょっとちがいますが、こう決めると、もとの道とまったく同じグラフになりますよね。
  • 一番下に書かれてる「0」は大体想像つくかと思いますが、ここに答えがでてきます。

みんな、Vimおねえさんといっしょに数えてみよう!

まずバッファに次のようなテキストを1行、書いておきます。

  • yl@=@0=="G"?"maGD\"=@-+1\rP`a":@0==" "?"r.l@1hj@1kh@1lk@1jr ":""

この行でddして、スタート地点にカーソルを持って行って、@1すれば Vimおねえさんががんばって数えてくれます

以下に、Vimおねえさんの勇姿を貼っておきます。

この通り、184だね!

たった1行で解けるVimおねえさん、すご〜い!

みんなのVimでも、かぞえてみよう!

同じように、あなたのお手元のVimでももちろん、数えることができます。ぜひ試してみましょう。

コピペするのもめんどいひとのために、すぐ試せるファイルを用意しておきました。

まずはこのファイルをダウンロードして、おもむろにお手元のVimで開いて、1行目にカーソルがある状態で ddjl@1 すれば 3×3 の道順の数が出てきます。

地形を適当にいじれば、4×4 も数えられますし、3×5 みたいな変則的な場合にも対応できます。スタートとゴールの場所を左上や右下以外にしても大丈夫です。 ただし、全体を「#」で囲うことと、最下行が「0」なのだけは変えないでくださいね。

補足

  • 枝刈りを一切やっていません。「どうあがいてもゴールに辿りつけない」状況がよほど明らかであっても、延々と探索を続けてしまいます。つまり、ほかのひとの大抵の実装より遅いはずです。
  • Vimおねえさんに組み合わせ爆発のすごさを教えてもらいたいみんなに残念なお知らせです。この方法で正確に数えられるのは 2,147,483,647 通りまでのようです。つまり、7×7 はむりです……
  • それでも、どーしても試したいという皆さんのために、Vimおねえさんロボットが数えてくれる 7×7 のファイルを用意しておきました。使い方は 3×3 のときと同じで、1行目にカーソルがある状態で ddjl@1 するだけです。
    • え、いつまでたっても終わらない?だいじょうぶ、きっとおわります。いつかは。
  • 前回のネタとかわりばえしてなくてごめんなさい

キーボードマクロで遊ぼう2011年12月25日 06:06

こんにちは。Vim Advent Calendar 2011 25日目の@plasterです。Vimって素敵なエディタですよね。

今日の記事のネタのメモをどこに保存していたか忘れてlocatefindgrepを重ねた挙句、 Vim起動して[CTRL-O]ですぐ見つかりました。知らなかったひとは今すぐ:h CTRL-Oしましょう。

エディタは生産性の源だと思います。

さて今日は、その生産性を効率的にむだづかいする方法の紹介です。いいんです、実用性などに縛られないからこその愛なのです。目指せ極北!

というわけで、かんたんなアスキーアート生成と、数列の生成と、迷路ソルバやります。全部キーボードマクロで

フィボなっち

まずはアスキーアートの生成から。見出しの「フィボなっち」というのは

(●´ー`●)
(●´ー`●)
(●´ー`●)(●´ー`●)
(●´ー`●)(●´ー`●)(●´ー`●)
(●´ー`●)(●´ー`●)(●´ー`●)(●´ー`●)(●´ー`●)
...

1行あたりの(●´ー`●)の個数が 1, 1, 2, 3, 5, ……とフィボナッチ数列になっているテキストです。

特になっちに思い入れがあるわけではないのですが、なんとなく和むAAなので、ちょっと気に入っています。 このテキストの入力を Vimで自動化してみましょう。

最初の2行は、そのまま入力します。
  • O(●´ー`●)[Esc]Yp
Oで新しく1行あけつつ挿入モードに入り、テキストを入力して[Esc]でモードを抜けます。 フィボナッチ数列は最初の2項が基本なので、Ypでコピペして2行にしておきます。

ステップ

今、バッファが

(●´ー`●)
(●´ー`●)

となっていて、2行目にカーソルがあるはずですね。ここで ykjpgJと入力すれば、次のようなバッファになります。

(●´ー`●)
(●´ー`●)
(●´ー`●)(●´ー`●)

カーソルは3行目です。ykjpgJを打つたびに、最後の2行をつなげたものが最終行に追加されます。たかだか6回の打鍵ですが、自動化してしまいましょう。

キーボードマクロの記録と呼び出し

qaでマクロの記録を開始します。その後qで記録を終了し、@aでマクロの呼び出しができます。たとえばqaykjpgJqと入力すれば、ykjpgJを実行しつつ記録するわけです。

(●´ー`●)
(●´ー`●)
(●´ー`●)(●´ー`●)
(●´ー`●)(●´ー`●)(●´ー`●)

以後、最終行にカーソルがある状態で@aを入力するたびに1行ずつ増えていきます。

(●´ー`●)
(●´ー`●)
(●´ー`●)(●´ー`●)
(●´ー`●)(●´ー`●)(●´ー`●)
(●´ー`●)(●´ー`●)(●´ー`●)(●´ー`●)(●´ー`●)

3@aとか打てば、3行ふえます。99@aとかももちろんアリ!ですが、すごいことになるので注意しましょう。万一やってしまって Vim が暴走しちゃったら、落ち着いて[CTRL-C]です。

フィボナッチ数列

コピペでひたすらテキストを増やしていく例を紹介しましたが、もちろん普通にフィボナッチ数列を作ることもできます。

……とその前に、[CTRL-A]を紹介します。あと、レジスタとマクロの関係を説明します。

CTRL-A

たとえば、バッファに

1

とある状態で、1の部分にカーソルを持っていって[CTRL-A]すると

2

になります。もう一度[CTRL-A]すると

3
になります。[CTRL-A]するたび1ずつ増えるね!ってことです。 10[CTRL-A]とすれば10ふえます。
13

マクロとレジスタ

さきほどはqaqを使ってキーボードマクロを記録しました。記録されたマクロは@aで呼び出すことができるわけですが、 実はレジスタに入っている内容ならば、なんでも呼び出して実行することができます

Vimしらない人がこの記事を読んでくださってるのか謎ですが念のためにひとことで説明しておくと、レジスタっていうのは、Vimにおけるクリップボードみたいなものです。 テキストをヤンクや削除したりキーボードマクロを記録したりすると、とにかく レジスタ というものに記憶されるんだと覚えておけば間違いありません。

ヤンク内容を実行できる機能は、バッファに「自動で実行したい内容(の一部)」が書かれているようなときに大変役立ちます。キーボードマクロで遊ぶときの基盤技術ともいえます。 たとえばバッファに

10

という行があって、この行の行頭にカーソルがあるとします。 y$すると無名レジスタ"10が入ります。 この状態で@"と入力すると、10を入力したのと同じことになります。続けて[CTRL-A]を入力すれば、全体では10[CTRL-A]したのと同じことになり、この行のテキスト1010インクリメントされて、次のように変化しているはずです。

20

この先の展開が丸見えの方もいらっしゃるかと思います。 準備できたので、フィボナッチ数列の生成をしてみましょう。

最初の2行はそのまま入力してしまいます。

  • O1[Esc]Yp

バッファは次のような2行になり、2行目にカーソルがあるはずです。

1
1

ステップ

フィボなっち のときと同様に、ykjpgJします。

1
1
11

カーソルは最下行の2桁目、最初の1の直後にあるはずです。この状態で、Dしましょう。

1
1
1

ご存知のとおりDは、カーソル位置から行末までを削除します。 これは1行未満の削除なので、小削除用レジスタ-に削除内容が入ります。 ここで@-することで、さきほど削除された1がVimに入力されている状態になります。さらに[CTRL-A]すると、残った部分がインクリメントされます。

1
1
2
全部1ばかりでわかりづらかったので、キーボードマクロを記録しがてら、もう1ステップ見てみましょう。3行めにカーソルがある状態でqaykjpgJすると:
1
1
2
12

続けてD

1
1
2
1

カーソルは4行目にあり、小削除用レジスタ-には2が入っています。 ここで@-[CTRL-A]と入力すれば、2[CTRL-A]するのと等価なので、4行目が2インクリメントされて

1
1
2
3

となります。qを入力してマクロ記録を終了して、以後@aと打つたびに、つぎつぎとフィボナッチ数がふえていきます。 10@aとかも余裕です。

1
1
2
3
5
8
13
21
34
55
89
144
233
377

迷路を解こう

ちょっと前、、、といっても2年近く経ってしまったようなのですが、迷路ソルバが流行ってましたよね。たしか、Vimスクリプトで解いてた方もいらしたように記憶しています。

私も実は anarchy golfmaze solving にあるものをVimで解いたことがあるので、その中身を紹介します。 ただし、

  • Vimといってもキーボードマクロで実装します。
  • 実装の都合上、以下の制限があります。
    • ナイーブな「深さ優先探索」するので、 迷路の形状によっては必ずしも最短経路になりません
    • 外壁が矩形になっていることを前提とします。
    • スタート地点(として扱うことのできる外壁の空き)が、 外壁の上辺または左辺にあることを前提とします。

(以下の設問および解答は anarchy golf からの引用です。)

こういう迷路があったとき (「#」が壁、空白が通行可能箇所)

############################# ###########
#   #   #       #     #     #     #     #
# # # ### ##### ### ### ### ##### # #####
# # # #   #   #       # #   #     #     #
# # # # ### # ####### # ##### ### ### # #
# # #   #   #   # #   # #     # # #   # #
### # ### # ### # # ### # ##### # ##### #
#   #     #   #   # #   # #   # # #   # #
# ########### ##### ### # ### # # # # # #
#   #       #     # #   # #     # # # # #
# # ##### ####### # # ### # ##### # # # #
  #       #   # # #     #       #   # # #
# ########### # # ##### ####### ####### #
# #   #       #   #   #   #   #     #   #
# # # ### ####### # ##### # ##### # # ###
# # #   #     #         #       # # #   #
# ##### ### ### ####### ####### # ##### #
# #       #   #     #   #     # #   # # #
# # ##### ### ##### # ### ### # # # # # #
#       #           #     #   #   #     #
#########################################

「.」で入口から出口までの経路を示すのがミッションです。

#############################.###########
#   #   #.......#     #     #.....#     #
# # # ###.#####.### ### ### #####.# #####
# # # #...#   #.......# #   #.....#     #
# # # #.### # #######.# #####.### ### # #
# # #...#...#   # #...# #.....# # #   # #
### #.###.#.### # #.### #.##### # ##### #
#   #.....#...#   #.#   #.#   # # #   # #
# ###########.#####.### #.### # # # # # #
#   #       #.....#.#   #.#     # # # # #
# # ##### #######.#.# ###.# ##### # # # #
..#       #   # #.#.....#.......#   # # #
#.########### # #.#####.#######.####### #
#.#   #       #  .#   #...#   #...  #   #
#.# # ### #######.# #####.# #####.# # ###
#.# #   #     #...      #.......#.# #   #
#.##### ### ###.####### #######.#.##### #
#.#.......#   #.....#   #     #.#.  # # #
#.#.#####.### #####.# ### ### #.#.# # # #
#...    #...........#     #   #...#     #
#########################################

設問に入口と出口の区別はないのですが、 とにかく「外壁のあいてる2箇所をつなぐ経路を出しましょう」ということですね。

解き方の方針

  1. スタート位置にカーソルを持っていき、
  2. カーソル位置の文字が空白 なら、以下 3. ~ 8. を順に実行する。 そうでなければ、何もしない。
  3. カーソル位置の文字を.で上書きする。
  4. カーソルをひとつ右に移動し、2. を再帰的に実行し、カーソルをひとつ左に移動する。
  5. カーソルをひとつ下に移動し、2. を再帰的に実行し、カーソルをひとつ上に移動する。
  6. カーソルをひとつ左に移動し、2. を再帰的に実行し、カーソルをひとつ右に移動する。
  7. カーソルをひとつ上に移動し、2. を再帰的に実行し、カーソルをひとつ下に移動する。
  8. カーソル位置に空白 を書き込む。

なお、途中でカーソルの移動が失敗したら(つまり出口にたどり着いていたら!)、大域脱出して実行終了ということにします。

実装

まずバッファに次のようなテキストを1行、書いておきます。

  • yl@=@0==" "?"r.l@1hj@1kh@1lk@1jr ":""

この行でddして、テキストをレジスタ1に入れます。 あとは、カーソルを迷路の入口

                             ↓ココ
############################# ###########
#   #   #       #     #     #     #     #

に持ってきて@1すれば、迷路が解けます。1行で解けるんですね、Vimってすごい!

実行してみた様子のキャプチャを以下に貼ります:
キーボードマクロな迷路ソルバ実行デモ

解説

肝になっているというかキモいのが、expression用レジスタ=です。 「式を評価して、その中身がレジスタに入っているものとして扱う」という代物で、ふつうは

  • (ノーマルモードで)"=7*6[Enter]p
  • (挿入モードで)[CTRL-R]=7*6[Enter]

のように、即席で計算式を書いて、その計算結果を文書中に張り付けたいときに活用すると便利なものです。 ところで、これももちろんレジスタなので、計算結果の値をマクロとして直接実行できます。それが@=なのです。(式にどんなものが書けるのか知りたい方は、今すぐお手元のVimで:h expressionしましょう!)

今回の@1で何が起こるか順を追って説明すると:

  1. ylでカーソル位置をヤンクし、レジスタ0に入れる。
  2. @=が来たので、続けて入力されるものを式とみなしてその値をマクロ実行する。
  3. @0==" "?"r.l@1hj@1kh@1lk@1jr ":""を評価し、その値をマクロ実行する。
    • カーソル位置が空白文字だった場合、 式の値は文字列"r.l@1hj@1kh@1lk@1jr "になるので、 r.l@1hj@1kh@1lk@1jr を実行する。これは前述「解き方の方針」3. ~ 8. に相当。
    • そうでなければ、式の値は空文字列""になるので、何も実行しない。

という具合に、素直な再帰実行しています。なお、マクロ実行はコマンドにひとつでも失敗すると即座に中止されます。どんなに深い再帰をしていても全部中断、つまり大域脱出してくれます。今回はこれがぴったりハマりました。

キーボードマクロとanarchy golf

私が expression用レジスタ のこのような使い方を知ったのは anarchy golf の Tower of hanoiVimnnさん に完敗したときです。 まさかキーボードマクロで素直な再帰が実現できるとは微塵も思っていなかったので目から鱗だったというか、その後の Vimゴルフをまったく違ったものにしてしまう、(少なくとも私の中では)強烈な事件でした。

今更ですが anarchy golf についてひとこと説明しますと、お題を満たすプログラムのソースを投稿して、ソースの短さを競うというものです。プログラムが正しいかどうかは、既定の(最大)3通りの入力に対して、正しい出力を出すこと、で判定されています。

Vimの場合は、「入力が予めバッファに入っている状態で、正しい出力の形にバッファ内容を変更する」ためのキー入力をプログラムとして与えます。それを1バイトでも短くするには、しばしば、キーボードマクロが大変有効なのです。

つまり何が言いたいかというと、キーボードマクロで遊ぶのに anarchy golf は最高ってことですね。 クリスマスに年越しに、Vimゴルフっていいものですよ。

  • 0が0回の繰り返しじゃなくて行頭移動なことにブチキレそうになったり
  • [CTRL-J][Enter]の挙動の違いに気づかずハマったり
  • :sで置換対象がなくてもエラーにしないために最後につけるeがもったいなかったり

いろいろ言い足りないことがある気がするのですが、またの機会ということで。 それではみなさん、(Vimと)よいクリスマスをお過ごしくださいませー。私はこれから寝ます(キリッ