はじめに
以前こちらでVimGolfが面白いという記事を書きました。本格的に進めていこうと思うのですが問題数が多いため、学んだことを忘れないよう自分用に解説を残しておきます。
あくまでVim力を高めるためのものでありトップVimゴルファーになるためのもではないので、回答しなかったものや最適解ではないものもありますが悪しからず。どちらかというとその問題を通して知ったVimの知識を残すものになります。
※何なら役に立つ場面がそんなに無い可能性もあります。
Vimカウントアップ
hello-world-vimgolfという問題についてです。Vimにはノーマルモードで数字の上で[count]<C-a>
を入力するとcount
の分だけ加算する機能があります。ヴィジュアルモードで複数行を選択している場合に[count]g<C-a>
とするとcount
分増加しながら加算されていきます。これを使うことで配列を簡単に作れます。
減算の場合は<C-x>
とします。この問題はこれで解きます。まず50行コピペをして、2行目から全選択でg<C-x>
が答えです。
51
51 ← ここから
中略
51 ← ここまで行選択
※数字のある行であれば数字の上にカーソルが無くても勝手に移動してくれます。(テキストオブジェクト)
※[count]
にはレジスタも利用可能です。例えば無名レジスタに2を格納しておいて@"<C-a>
とすると2加算します。
※ちなみにリピートコマンドでも上は当然適用されます。
Vimソート
Reordering Lorem IpsumはVimソートを使うと簡単です。:%sort/...../
とすると6文字目を評価して昇順にソートすると簡単に並び替えられます。今回は正規表現を使用していますがパターンマッチも可能です。
どんな複数行のランダム文字列が来たとしても、必ずどこかの列にソート出来る場所はあるので応用の効く解放じゃないかなと思います。
正規表現置換・グループ化
Fill in the chess boardはVim置換機能でのグループ化を使ういい機会です。行頭の各数字を利用してa~hの後ろに付与するというのを繰り返すというものです。Exコマンドモードにて下記のコマンドを実行して下さい。
:%s/^\([0-9]\)/\1 a\1 b\1 c\1 d\1 e\1 f\1 g\1 h\1/
「&」がマッチした文字に置き換わるというショートハンドの様な機能もあるので、より少ないキーストロークでも可能です。
:%s/^[0-9]/& a& b& c& d& e& f& g& h&/
また置換対象の行範囲を指定出来ることも応用が効きます。
※2行目から5行目までが対象
:2,5s/./$ a& b& c& d& e& f& g& h&/
※2行目から最終行までが対象
:2,$s/./$ a& b& c& d& e& f& g& h&/
Vimマクロ(しかもネスト)
Fill in the chess boardの別解なのですがVimマクロをネストするという解法も面白いです。Vimマクロが何か分からないという方は先にこちらをお読み下さい。非常に便利です。レジスタを指定してヤンク出来るVimの性質を利用することでVimマクロを実行するVimマクロを登録する事が可能になるというものです。
ノーマルモードにて下記のVimマクロを登録・実行するというのが別解にありました。(下のVimマクロは概要の解説です。足りない部分は自身で補足して下さい。)
※一行下に移動して直前に無名レジスタを貼り付けるVimマクロを「w」レジスタに登録
qwjPq
※カーソル下の一文字をヤンクした後先程のVimマクロを8回実行してから9文字上3文字右に移動するVimマクロを登録
qqyl8@w9k3lq
Vimヘルプ最適化
お次はLearn to ask for :helpという問題です。Vimmerたるものヘルプには精通していなくてはいけません。(読み始めたのは最近ですが)
下記Exコマンドでbars項目に飛ぶ事が可能です。
※ちなみにモード毎に細かい検索プレフィックスなどもあるので把握しておくと良いです。
:h bars
また:h
で別ウィンドウでヘルプを立ち上げているとして、その状態で:h bars
としてもさらに新しいウィンドウで開かれる事はなく、既存のウィンドウで検索されます。
組み込み関数
続いてPlotting some variables in pythonです。Vimには組み込みで使える関数があるのですが、この関数を:substitute
でも利用することが出来ます。つまり下記の置換を実行すると文中の「1」が「行番号-1」に置き換わります。
:%s/1/\=line('.')-1/g
コマンドラインからノーマルコマンド
Plotting some variables in python問題には別解があり、:norm
というExコマンドを実行していました。
:2,$norm fyiabs(<C-O>f,)<CR>
範囲指定をしてノーマルモードでの入力をコマンドラインウィンドウから実行出来るということになります。
コマンドラインモードでレジスタ操作
Transpose a python matrixという問題です。いわゆる転置です。そこまでスコアの高い解法ではないのですがレジスタを書き換えるというテクニックは面白いと思いました。一列目から順番に処理を考えればカウントアップすれば良いことになりますよね。つまり最初に「1」をレジスタxに貯めておき、
レジスタxを貼り付ける→レジスタxを1増やす→下に移動する
というマクロを繰り返すことで自動化出来るのです。
※「1」を選択してレジスタxに格納
v"xy
※レジスタxを貼り付ける
<C-R>x
※コマンドラインモードでレジスタxの値を1増加
:let @x=@x+1
マクロの中でもコマンドラインモードでレジスタ操作が可能です。
ドットコマンド
Transpose a python matrixの別解です。この問題に限らずVimといえばドットコマンドです。今更言うまでも無いですがドットコマンドとは直前の変更処理を繰り返します。ここでは<Ctrl-a>
のカウントアップ処理をドットコマンドで繰り返します。
またテキストオブジェクトにより数字の上にカーソルが置いてなくも次の数字に移動して自動で加算してくれる性質を利用して、カーソル移動を減らす工夫をします。
繰り返し貼り付け
Create a diamond commentという問題です。この問題で初めて知ったのですがa
やA
は前に[count]を付けられて、[count]分入力する文字を繰り返すことが出来ます。
つまり15a
と入力すれば15文字空白を入力出来ます。
※15i
ならカーソル位置から15文字空白を入力する。
グローバルコマンド
Create a diamond commentの別解です。:[range]g/pattern/[cmd]
コマンドは置換でよく使われますが、本来はパターンにマッチした行に対してExコマンドを実行するというコマンドです。
まず下記の状態を作成します。
#
# #
# #
# #
# #
# #
# #
# #
# #
#This is some text#
#
# #
# #
# #
# #
# #
# #
# #
# #
11行目で:.,$g/^/m10
とすると11行目から最終行までを順に10行目の下に移動する処理が実行されてダイヤモンド型に整形されます。
※ちなみに:[range]g!/pattern/[cmd]
だとパターンにマッチしない行にExコマンドを実行します。
※[range]
を省略すると(1,$)
つまりバッファ全体になります。
Vim置換で特別な意味を持つ文字クラス
Lowercase first charactersについてです。Vim置換において個性的(?)な文字クラスが存在します。
\u | 次の文字が大文字になる | s/\u |
\U | 後に続く文字が (\E まで) 大文字になる | s/\U |
\l | 次の文字が小文字になる | s/\l |
\L | 後に続く文字が (\E まで) 小文字になる | s/\L |
これを使い下記コマンドを実行するとアルファベットを小文字にして後ろに「-」を付けて置換します。
:%s/\(\a\)/\L\1-/g
組み合わせると複雑な置き換えもスマートに行なえます。
"BLA bla" に
:s/\w\+/\L\u\0/g を実行すると
"Bla Bla" に置き換わる
文字コード
Migrated to Postgres!という問題です。Vimでは絵文字などの特殊な文字列を直接文字コードから入力することが可能です。挿入モードで<Ctl-v>
を入力すると文字コードを直接指定したことになります。<Ctl-v
の後にU
を入力するとUnicodeの文字コードを指定します。
<Ctl-v>U1f308
置換(戒め)
simple replacementsです。何ということのない問題ですが:substitute
の正規表現のORってどうやるんだっけ?となってしまったので自戒の念も込めて備忘録です。
:%s/substitute\|emacs/vim/g
ヴィジュアル選択検索
同じくsimple replacements関連です。n
で前回のパターンで前方検索が出来ます。gn
とすると検索した後にマッチした文字列をヴィジュアル選択します。ドットコマンドと組み合わせると相性が良いです。
例えば一回検索してからcgn
で「vim」を入力するまでがドットコマンドで繰り返せるという寸法です。
置換繰り返し
更に同じくsimple replacements関連です。:~
とすると同じ置換文字列で最後の検索パターンで置換処理を行えます。これは下記のExコマンドと同義です。
:s//~/
これを全行に実行するにはg&
とします。毎回:substitute
コマンドを作らなくて済みます。
vglobalコマンド
次はInverting Linesという問題です。先述のグローバルコマンドと全く同じ解法で解けるのですが、:vglobal
もしくは:v
を知っておくとこの先も役に立つかも知れません。:g
は指定したパターンにマッチした行に後続の処理を施しますが、:vglobal
はマッチしなかった行に処理を行います。
※:g!
と同義です。
この問題だと「V」が一行目にしか含まれないので次の様なコマンドが回答になります。
:%v/V/m1
Vim標準の補完機能
CSV to MD formatという割と汎用性のありそうな問題です。この問題で思い出したのですがVimには標準で補完機能があります。挿入モードで<CTRL-N>
と入力するとカーソル直前のキーワードで始まる単語を前方一致で検索して、存在していると入力します。マッチする単語が複数存在している場合は抽出されたものの中から選択するUIが表示されます。
※この補完機能はキーストロークはたしかに減らせますが、あまり使い勝手は良くないです。
インデント関連の話
同じくCSV to MD formatです。>
でインデントを追加出来ます。[count]>
で行数を指定出来ます。>
の後にモーションを続けられるのですが、下に移動しようとするとインデントされる行が増えてしまうので>>
としています。
また下記の設定についてです。
set expandtab
既に設定しているかと思うのですが、挿入モードでのタブ入力時に空白を入れてくれる設定です。有効にしておくと>
によるインデントも空白にしてくれます。
Ordinary Atoms is 何?(多分文字クラス)
同じくCSV to MD formatです。検索や置換で使える文字クラスを上手く使っていた解法が勉強になりました。Vim Helpの項目名が「Ordinary Atoms」とあったのですが翻訳出来ませんでした;;
下記の状態で、
flower
jasmine
rose
fruit
orange
cherry
apple
vegetable
ginger
garlic
onion
下記のExコマンドを実行すると
:%s/\</- /
下記の通りに置き換えられます。
- flower
- jasmine
- rose
- fruit
- orange
- cherry
- apple
- vegetable
- ginger
- garlic
- onion
\<
が行内の単語の先頭にマッチするのを利用しています。その他の文字クラスについては下表の通りです。3~4行等はよく使うのではないでしょうか。
\< | 単語の先頭 |
\> | 単語の末尾 |
^ | 行頭 |
$ | 行末 |
小技
別段Vim力には影響し無さそうだけどVimGolfでよく使われる小技もまとめておきます。
ZZ
ファイルに変更があれば保存して終了する。変更が無ければ終了する。
Y
yyと同義。
y{motion}
{motion}の文字列をレジスタにコピーする。
<C-y> | <C-e
挿入モードにて上(下)の行の文字を挿入する。