Vimでダミーデータを作る

そんなプラグインを書きました。この記事はその使い方の紹介です。

ダミーデータというのは、適当な名前とか、いわゆる _Lorem ipsum_ みたいな無意味な文章といった穴埋め用途のテキストのことです。

```vim
:echo fake#gen("paragraph")

Ignota autem nostrud albucius sagittis no fabulas erat eam ridens
et per moderatius 98 movet. Sea iure est integre metus adipisci
justo id con ultrices omnes minim odioaccommodare. Consul omnium
enim volumus lectus habeo fuisset pri utinam sem tamquam no.
```

## 簡単な使い方

適当なプラグインマネージャーでインストールします。
[https://github.com/tkhren/vim-fake](https://github.com/tkhren/vim-fake)

```
NeoBundle 'tkhren/vim-fake'
```

### 生成してみる

`fake#gen({keyname})` という関数を呼ぶと、実行ごとに異なる答えが返ってきます。
`{keyname}`にはいろいろありますが、例えば国の名前が得たいなら、`"country"` を使います。

```vim
:echo fake#gen("country")
Canada
:echo fake#gen("country")
Nigeria
:echo fake#gen("country")
Indonesia
```

### 挿入してみる

挿入モード中にダミーデータを生成したいなら、 _Expression register_ を使います。

```vim
<C-r>=fake#gen("country")
```

（適当なスニペットプラグインを使えばもっと簡単に挿入できます。）
参考: [vim-fake & neosnippet example - Gist](https://gist.github.com/tkhren/9fbc7227c2e9d2f4319c)
![fakesnip](https://cloud.githubusercontent.com/assets/534457/13453296/34c4895a-e092-11e5-83d3-d81afee83c37.gif)


### 置換してみる①

次のテキストの `dummy` を男性の名前（male_name）で置換したいなら、

```xml
<ul>
    <li> dummy </li>
    <li> dummy </li>
    <li> dummy </li>
</ul>
```

`:%s/dummy/\=fake#gen("male_name")/g` を実行すると`dummy`がテキトーな人の名前に変わります。

```xml
<ul>
    <li> Steve </li>
    <li> Rodney </li>
    <li> Leonard </li>
</ul>
```

### 置換してみる②

いちいち置換コマンド打つのは面倒なとき、大量のテキストを生成したい時には、
プレースホルダーを使うのが便利です。

まず、編集中のファイルに `FAKE__{keyname}__` を書いておきます。

```xml
<user>
	<name>FAKE__fullname__</name>
	<age>FAKE__age__</age>
	<job>FAKE__job__</job>
</user>
<user>
	<name>FAKE__fullname__</name>
	<age>FAKE__age__</age>
	<job>FAKE__job__</job>
</user>
```

`:FakeSubstitute` を実行すると、`FAKE__{keyname}__` のそれぞれを `fake#gen({keyname})`で一括置換します。

```xml
<user>
	<name>Lydia Dawson</name>
	<age>59</age>
	<job>Dentist</job>
</user>
<user>
	<name>Charlie Adams</name>
	<age>42</age>
	<job>Musician</job>
</user>

```

## データソース（keyname）の選択

ランダムデータの生成には `fake#gen({keyname})` を使います。
この `{keyname}` には使いたいデータの名前を指定します。デフォルトでは次のものが用意されています。

| {keyname}     | 内容          | 並び順      |
|:--------------|:-------------|:-----------|
| male_name     | 世界の男性名   | 人口順     |
| female_name   | 世界の女性名   | 人口順     |
| surname       | 世界の苗字    | 人口順     |
| country       | 国の名前      | 人口順     |
| gtld          | gTLD         | 登録数順   |
| job           | 仕事の名前     | &nbsp;   |
| word          | 頻出英単語     | &nbsp;   |
| nonsense      | 意味のない単語  | &nbsp;   |

これらのデータソース（辞書ファイル）は、UNIXの`/usr/share/dict/words`のような単語が行ごとに羅列されただけの単純なテキストファイルです。

例えば `country` 辞書には、世界の国の名前が書いてありますが、`fake#gen("country")` すればこのファイルの中のランダムな行が返されます。CanadaとかBrazilとかが返ってきます。選択する行は一様にランダムです。

```vim
:echo fake#gen("country")
China
```

### 辞書ファイルの追加

この辞書ファイルはいくつでも任意に追加することができ、globしてはじめに見つかったファイルを使います。`g:fake_src_paths` には辞書ファイルの入ったディレクトリを指定します。

```vim
let g:fake_src_paths = ['/home/user/.vim/fake_src1',
					    \ '/home/user/.vim/fake_src2']
```

このソースディレクトリは階層にすることもできるので言語や用途ごとに分けたりできます（`fake#gen("ja/male_name")`のようにアクセスできる）。

## データ生成を定義する

本領はここからです。 ***辞書ファイルを組み合わせることで、いろいろなデータの生成を定義することができます。***

`fake#define({keyname}, {code})` という関数を使って新しい`{keyname}`を定義します。

`fake#gen({keyname})` は`{keyname}`が定義されていれば、`eval({code})`を返し、もし定義されていなければ、`g:fake_src_paths`に辞書ファイルを探しに行き、そのランダムな行を返します。


どんな組み合わせができるかというと、例えば


```vim
"" リストからランダムにひとつ要素を返す
call fake#define('sex', 'fake#choice(["male", "female"])')

"" 男性と女性の名前を得る
call fake#define('name', 'fake#int(1) ? fake#gen("male_name")'
                                \ . ' : fake#gen("female_name")')

"" フルネームを得る
call fake#define('fullname', 'fake#gen("name") . " " . fake#gen("surname")')

"" 世代人口に沿った年齢を返す
call fake#define('age', 'float2nr(floor(110 * fake#betapdf(1.0, 1.45)))')

"" 国の人口割合に応じて国の名前を返す（China, Indiaが頻繁に返る）
call fake#define('country', 'fake#get(fake#load("country"),'
                        \ . 'fake#betapdf(0.2, 4.0))')

"" 登録ウェブサイトの数に応じたgTLDを返す（com, netあたりが頻繁に出る）
call fake#define('gtld', 'fake#get(fake#load("gtld"),'
                        \ . 'fake#betapdf(0.2, 3.0))')

"" メールアドレスを生成
call fake#define('email', 'tolower(printf("%s@%s.%s",'
                        \ . 'fake#gen("name"),'
                        \ . 'fake#gen("surname"),'
                        \ . 'fake#gen("gtld")))')

"" Lorem ipsum みたいな意味不明な文章を生成
call fake#define('sentense', 'fake#capitalize('
                        \ . 'join(map(range(fake#int(3,15)),"fake#gen(\"nonsense\")"))'
                        \ . ' . fake#chars(1,"..............!?"))')

call fake#define('paragraph', 'join(map(range(fake#int(3,10)),"fake#gen(\"sentense\")"))')

"" エイリアスを作るならこんな感じ
call fake#define('lipsum', 'fake#gen("paragraph")')

```

このようなコードをvimrcあたりに書いておけば、`fake#gen("fullname")` のような形で呼び出すことができます。上のコードは `:let g:fake_bootstrap = 1` を定義すれば勝手に読み込まれ、デフォルトは 0 です。好みに応じてONにしてください。

## 生成方法に偏りをつける（重み付け乱択）

`int()`とか`capitalize()`などは命名からなんとなく分かるかもしれませんが、それ以外にも幾つか知らない関数が出てきたと思います。

```vim
"" 辞書ファイル（{dictname}）の行のリストを返す（内部的に一時キャッシュされる）
fake#load({dictname})

"" listからランダムな要素を返す。
fake#choice({list})

"" リストの要素を相対位置で取得する。rateは0.0 ~ 1.0
"" 例えば rate=0.0 ならリストの先頭要素を返す, 0.5 ならリストの中央の要素を返す
fake#get({list}, {rate})

"" a,bで与えられるベータ分布を返す（形状パラメーターa,b > 0, 返り値は0.0 ~ 1.0）
fake#betapdf({a}, {b})
```

`choice()` は与えられたリストからランダムに要素を取り出します。取り出し方は一様であり、偏りはありません。


重み付けされた要素選択をしたい場合は、`get()`と`betapdf()`を組み合わせます。`get()`は第二引数に取り出したい要素のインデックスを相対位置で指定できるため、0.0~1.0を返す`betapdf()`を指定すると、リストのどのあたりの要素を頻繁に取り出したいかを指定できます。

`betapdf()`は 0.0 ~ 1.0 を返すベータ分布の確率密度関数で、一様ではない偏った乱数を得ることができます。パラメーターa,bの取り方は、次の記事を参考にしながら決めてください。

http://www.ntrand.com/jp/beta-distribution/
http://keisan.casio.jp/exec/system/1161228837


実は `country` 辞書は人口の多い順に国名が並んでいます。なので人口の多いChinaとかIndiaを頻繁に欲しいなら、次のように書けばリストの先頭要素(辞書ファイルの先頭要素)を多く選択するので、それっぽくシュミレーションできます。 _※厳密な人口比率に沿った乱択をするわけではありません。_

```vim
"" 国の人口割合に応じて国の名前を返す（China, Indiaが頻繁に返る）
call fake#define('country',
	\ 'fake#get(fake#load("country"), fake#betapdf(0.2, 4.0))')
	
:echo fake#gen("country")
China
:echo fake#gen("country")
India
:echo fake#gen("country")
China
```

下図 `betapdf(0.2, 4.0)` のグラフ
![87746340248.png](https://qiita-image-store.s3.amazonaws.com/0/67544/911d3e93-b76f-cf41-de7b-e55dac920ffe.png "87746340248.png")


## その他
#### 乱数の精度
Vimには組み込みの乱数生成関数が無いので、`sha256()`を10進数に直して苦し紛れに乱数としています。そのため、MTとかXorShiftと比較すれば一様性は非常に悪いと思います。それでも今回の用途としては十分な品質と考えているので、Vital.vimにXorShiftがありますが結局使いませんでした。

#### データソースの内容について
デフォルトの辞書ファイルは使い方を説明するためだけにバンドルしているようなもので、内容が充実しているとは言えませんし正確性も保証できません。なので今後勝手に内容更新したりする可能性もあります。内容に不備・不満等があれば、自前で辞書を用意したほうが早いと思います。