Rでテスト駆動開発っぽいことをしたい

# 背景

プログラム開発プロセスを見直したいと思っていた。
変数の名前やコメントの入れ方など、色々勉強＆工夫はしているが、根本的な方法論が不足しているのは明らかだった。
データ分析系の仕事では、製品開発のプログラミングスタイルは個人の生産性が低く、明らかに合わない。
だが、検証やドキュメンテーションの不備は、たいてい納期直前に地獄を見ることになる。
この板挟みをうまく解消する方法はないだろうか？

そこで、最近、以下の記事を読んだ。
[忙しい研究者のためのテストコードとドキュメントの書き方](https://qiita.com/NaokiHamada/items/0689cd85fb3e1adcda1a)

これは非常にいい。Rでも実践したいと思った。


# RStudioの機能を使う

まずPythonのdocstring的なものを用意しないといけない。
幸いなことに、RStudioでは、関数の中でCtrl+Alt+Shift+Rでdocstring的なもの (Rで別の名前がついてるのだろうか？) を作れる。
関数の引数を書いて、関数の中で上記コマンドを押せば以下のようになる。

```r
#' Title
#'
#' @param x
#' @param y
#'
#' @return
#' @export
#'
#' @examples
comp_eigen <- function(x, y) {

}
```

ここから開発をはじめる。
以下、作業は全てRStudio上で行う前提。


# docstringを書く (入出力編)

関数のやること、引数、戻り値を書く。

```r
#' 2つの行列の固有値の内積を求める
#'
#' @param x 行列1 (matrix)
#' @param y 行列2 (matrix)
#'
#' @return 内積の値 (num)
#' @export
#'
#' @examples
comp_eigen <- function(x, y) {

}
```

これで入出力を定義できた。
普段はこれでいいが、もし将来的にパッケージ化する場合、英語じゃないとだめ (というか2バイト文字がだめ) っぽいので、気をつける。


# docstringを書く (テストコード編)

次に、@examplesの下にこの関数を使った処理を書く。
ここで書く処理は、サンプルであり、テストコードになる。

```r
#' 2つの行列の固有値の内積を求める
#'
#' @param x 行列1 (matrix)
#' @param y 行列2 (matrix)
#'
#' @return 内積の値 (num)
#' @export
#'
#' @examples
#' set.seed(0)
#' x <- matrix(rnorm(9), 3, 3)
#' y <- matrix(rnorm(9), 3, 3)
#' comp_eigen(x, y)
comp_eigen <- function(x, y) {

}
```

本当はcomp_eigen()の出力値に関する想定も入れた方が良いのだが、今回はなしで（動作確認までしかテストしないことになる）。
答えがわかったら、コメントに追記しておくと、将来的なアップデートをした際に結果を検証できるので良いだろう。
ちなみになぜこれがテストコードになるかだが、@examplesより下の部分にカーソルをあわせてCtrl + Enterを押すとその行を実行できる。
とても便利な機能だが、知ったのはつい最近だ（むしろこれを知ってればもっと前から@examplesにテストコード書いてたよ……）。

# 関数の中身を書き込む

中身を書き込んでいく。

```r
#' 2つの行列の固有値の内積を求める
#'
#' @param x 行列1 (matrix)
#' @param y 行列2 (matrix)
#'
#' @return 内積の値 (num)
#' @export
#'
#' @examples
#' set.seed(0)
#' x <- matrix(rnorm(9), 3, 3)
#' y <- matrix(rnorm(9), 3, 3)
#' comp_eigen(x, y)
comp_eigen <- function(x, y) {
  x_value <- eigen(x)
  y_value <- eigen(y)
  x_value*y_value
}
```

中身を一通り書けたら、次のステップに移る。

# テストする

関数を読み込んだ後、@examplesの行をCtrl+Enterで実行する。
結果が正しく出力されるか、どんなエラーが出るかを確認する。

```r
> comp_eigen(x, y)
Error in x_value * y_value : non-numeric argument to binary operator
```

エラーが出てきた。最後の行がおかしいらしい。

# デバッグする

`browser()`を使ったりして、途中の状態を確認しながら直していく。

よくみると、そもそもx_valueの値が思ったのと違うようだ。固有値ではなく、リスト構造になっている。

```r
Browse[1]> x_value
eigen() decomposition
$values
[1]  1.0382130+1.21338i  1.0382130-1.21338i -0.4045975+0.00000i

$vectors
                     [,1]                 [,2]         [,3]
[1,] 0.7280958+0.0000000i 0.7280958+0.0000000i 0.1940062+0i
[2,] 0.0169427+0.2529073i 0.0169427-0.2529073i 0.3995693+0i
[3,] 0.1994380-0.6048569i 0.1994380+0.6048569i 0.8959386+0i
```

ここから欲しいのは固有値(`$values`) だけだ。
ここを修正すると、同様にy_valueも修正することになる。
その後、戻り値が3つ返ってきて「あれおかしいぞ？」となり、内積を求める計算も、`*`でもないということに気づく。
最終的に、以下のようになる。

```r
#' 2つの行列の固有値の内積を求める
#'
#' @param x 行列1 (matrix)
#' @param y 行列2 (matrix)
#'
#' @return 内積の値 (num)
#' @export
#'
#' @examples
#' set.seed(0)
#' x <- matrix(rnorm(9), 3, 3)
#' y <- matrix(rnorm(9), 3, 3)
#' comp_eigen(x, y)
comp_eigen <- function(x, y) {
  x_value <- eigen(x)$values
  y_value <- eigen(y)$values
  x_value %*% y_value
}
```

# 一通り動くようになる

テストコードを実行すると、以下の出力になる。

```r
> comp_eigen(x, y)
                   [,1]
[1,] 1.688619+3.510959i
```

というわけで、ひとまず完成した。

# リファクタする

今回はあまり必要ないと思うが、ある程度以上の複雑なプログラムを書いていると、リファクタが必要になるケースがほとんどだ。
リファクタ自体は、もともとやっている（はずな）ことなので、このプロセスにおいても当然必要だ。

# 完成

以上のプロセスで、めでたく「関数、ヘルプ、テストコード」の3つ組が完成した。
これなら半年後の自分もこのコードを見て絶望することはないだろう。

# 余談

実際には、テスト～デバッグは何周もグルグルすることになる（上記の例では、x_value,y_valueの修正と、内積の計算の修正で2周）。
何周するかは、プログラムの複雑度と、事前の設計、スキルなど様々な要因に依存する。

上記のやり方を実践した結果、今までに気になっていた点はかなり解消できた。あと完成した時に気持ちが良い。
一方で、以下のスライドのつまづきに見事にハマり始めた。レガシーコードの谷は非常に深い。
理想のプログラミングスタイルの実現はまだまだ遠いらしい。

[TDDを実践してわかったTDDつまづくあるあると自分なりの乗り越え方まとめ] (https://www.slideshare.net/keiswd/tddtdd)