複利計算?それVimでできるよ!〜マクロとレジスタの復習〜

Vimでマクロを使って複利計算をしつつ、レジスタの復習をします。
2020.01.30

こんにちは、ターミナル住人の平野です。

年も明けて早1ヶ月。 年度末も近くなり(弊社の期末は6月ですが)、お金の計算などをする機会が増える時期です。 私も色々とお金のことを考える機会があったのですが、 お金の運用なんかを考えると出てくるのが複利ですよね。

Vimをお使いの諸兄におかれましては、 複利計算などに表計算ソフトなどは使わずVimでさっと行うのが習わしでございますね。

今回はレジスタの紹介という含みも持たせつつ、 マクロを使用して複利計算をVimで行う方法をご紹介します。 もちろん複利以外にも漸化式的に計算するものに応用できますのでご活用ください。

マクロ

Vimでマクロを使うにはノーマルモードでqqを押します。 マクロの記録モードに入るので、繰り返したいコマンドを一通り実行し、 qを押して記録を終了します。

今回の場合「現在の値にある係数を書けた数を次の行に書く」 という一連の操作をマクロとします。

先に書いておくと、今回作成するマクロ全体は

yiWo[^r]=[^r]0*1.05[ENTER][ESCAPE]

です。 [ ]は特殊な入力を便宜的に表現しているだけで、 実際に[などを入力するわけではありません。

複利計算してみる

はじめの値を10000、利率を5%とします。 年利5%の定期預金とかあったら仕事辞めるわ!(辞められるほど資産ない)

スタート地点としては10000だけが書いてある状況です。

10000

qqでマクロの記録を開始します。このあとは箇条書きで。

  1. yiW
    • テキストオブジェクトで現在の単語(スペースで区切られた部分)をヤンクする
    • これで10000がレジスタ(クリップボードみたいなもの)に記録される
  2. o
    • 次の行に移動しつつインサートモードに入る
  3. [^r]=[^r]Ctrl+rを表す)
    • 算術計算をして、その結果を挿入するモードに入る
  4. [^r]0*1.05[ENTER][ENTER]Enterキーを表す)
    • [^r]0で、1.でヤンクした値を書き出す
    • Enterする直前の状態は10000*1.05
  5. [ESC][ESC]Escapeキーを表す)
    • インサートモードを抜ける

マクロは以上で完成なので、qを押してマクロ記録を抜けます。

以上を行ったところで、実際に次の行に5%を加えた値が入ります。

10000
10500.0

これで次の1行を求めるマクロが得られたので、あとはこれを繰り返すだけです。 10500.0の行で@qと入力するとマクロが実行され、更に次の行が入力されます。 あとは@qを目的の回数繰り返しても良いですが、 その回数が多い場合には100@qのように回数を指定することで連続した適用が可能です。 20回ほど繰り返すとこんな感じです。

10000
10500.0
11025.0
11576.25
12155.0625
12762.815625
13400.956406
14071.004226
14774.554437
15513.282159
16288.946267
17103.39358
17958.563259
18856.491422
19799.315993
20789.281793
21828.745883
22920.183177
24066.192336
25269.501953

単利だと500*20=10000しか増えないのに、16500くらい増えてますね! (こんな妄想してると逆に悲しくなってくる...)

「現在の行の情報を使って次の行の数値を入力する」というマクロを適用していますので、 考え方としてはExcelで上のセルの値を参照して計算しているのと同じですね。 ポイントとしては[^r]0の部分で、マクロとしては10000を入力する訳ではなく、 「その時のレジスタの値を貼り付ける」を組み込むことができています。

四捨五入などの関数の利用

[^r]=内の計算ではround()などの関数も使えるので、 計算部分については必要に応じて関数なども活用しましょう。

[^r]=についてはあまり使用頻度は高くありませんが、 算術的な計算結果だけでなくVimの関数も展開できますので、 内部的な値を貼り付けるのなんかにも使えます。
例えば現在編集中のファイルのフルパスを貼り付ける場合はこんな感じです。

=expand('%:p')

レジスタについて

さて、上記の一連の流れの裏で暗躍していたレジスタについて軽く触れようと思います。

ヤンクの裏にレジスタあり

ヤンクとは、普通に言えばコピーのことです1。 コピーされた文言はレジスタに格納されます。

例えば上記の例で、最初にyiWした際にヤンクした文字列100000(という名前の)レジスタに格納されます。 明示的に行ったヤンクの内容は0レジスタに格納されることになっています。

これを貼り付けるには基本的にはpを押せば良いわけですが、 正確にはこのpは「"レジスタ(一時レジスタ)を貼り付ける」というコマンドなので、 明示的に0レジスタを貼り付けたい場合は"0pとする必要があります。

明示的にレジスタを指定して使用する場合は"(レジスタ名)をコマンドの前に置きます。 なので、"ayyとすると、「現在の行をaレジスタにヤンクする」となるわけです。 同様に"apとすると「aレジスタの内容を貼り付ける」となるわけです。

マクロの裏にレジスタあり

qqでマクロ記録モードに入りましたが、これは 「qレジスタに(コマンドを)記録する」という意味です。 同様にqaaレジスタに、qbbレジスタに、です。

記録終了後、@aすると、aレジスタから文字列を読み取って、 それをキーシーケンスとして実行します。 文字列として貼り付けるのではなく、ノーマルモードのコマンド列として、 実際にキーボードで同様に打ったのと同じことが起こります。

レジスタの確認

コマンドモード(:を押した状態)で

registers

を実行すると、レジスタの中身を確認できます。 qレジスタの部分を見てみると、先ほど使ったマクロがそのまま格納されています。2

レジスタに格納されるのはあくまでも文字列で、用途は使うときに指定するだけなので、 ノーマルモードで"qpとすれば、この文字列が普通に貼り付けられます。

個人的には、この挙動を理解するとレジスタというものの本質がわかる気がします。 すなわち、レジスタはただの文字列(バイト列)で、

  • qa: 次にqが押されるまでのキーシーケンスをレジスタに記録する
  • @a: レジスタ内容をキーシーケンスとして実行する
  • "ay: 格納するレジスタを選択してヤンクする
  • "ap: レジスタを選択してそれをペーストする

などの動作をする際の、ただの一時的な格納場所という位置付けです。

レジスタの中身は上記の用途に汎用的に使えるので、@0 (ヤンクしたときにデフォルトで格納されるレジスタをキーシーケンスとして実行) なんてこともできます。 が、これは予期せぬ動作になることが多いので、 レジスタの中身をよく確かめてから実行するのが吉です。

まとめ

Vimのマクロを使うことで、こんなこともできるよ、という話でした。

「できるよ」と言っているだけで、 これがベストなやり方だとは言っていないです!w ので、ある程度の規模があるものはExcelやスプレッドシートを使いましょうね。

入り口はネタ的な感じでしたが、 レジスタの使い方などを把握しておくとVim力がグンとあがること間違いなし! この辺は素のVimの機能なのでどんな環境でも使えると思います(純粋なviは除く)。

それでは、良いVimライフを!


  1. CopyだとChangeと頭文字が被るのでYankになったとどこかで見た記憶。違うかも。 
  2. ^Cは、Ctrl+cでEscapeとは一応別ですが、今回は特に触れません。