R でプログラムスライシング #rstatsj

  • 15
    Like
  • 1
    Comment
More than 1 year has passed since last update.

使おうとした R の関数がうまく動かないときなどに、関数の中身のコードを見てみることって、よくありますよね。
しかし、R の関数の中身って複雑なことが多いです。
例えば、ベクトルから最初の n 個の要素を取り出す head() 関数があります。

R
head(1:10, n = 3)
結果
[1] 1 2 3

この head() 関数はジェネリック関数1なので、最初の引数のクラスによって、実際に呼び出される関数が異なります。
head(1:10) が実行された場合、実際に呼び出されるのは、utils:::head.default() 関数です。
ではこの utils:::head.default() 関数の中身を見てみましょう。

R
utils:::head.default
結果
function (x, n = 6L, ...) 
{
    stopifnot(length(n) == 1L)
    n <- if (n < 0L) 
        max(length(x) + n, 0L)
    else min(n, length(x))
    x[seq_len(n)]
}

最初の n 個の要素を取り出すだけなのに、複雑すぎませんか?
コードをよく見てみると、二行目の if(n < 0L)n が 0 より小さいかどうか判定しています。
これは、head() に渡す n をマイナス値にすると、後ろから -n 個を取り除くという動作に変わるためです。

R
head(1:10, n = -3)
結果
[1] 1 2 3 4 5 6 7

しかし、関数の動作を追いたいときは、こういった裏ワザ的な動作はどうでもよくて、自分の実行したい動作に関係ある部分だけを見たいですよね。

というわけで、先日作成した fixer パッケージに、そんな機能を追加してみました。

まずは fixer をインストールします。

R
install.packages("devtools")
devtools::install_github("hoxo-m/fixer")

追加したのは、fix_slicing() という関数です。
これは、関数に引数を指定すると、その引数を固定した関数を作成してくれる関数です。
実際の動作を見た方が手っ取り早いです。

R
library(fixer)
fix_slicing(utils:::head.default(n = 3))
結果
function (x, `n = 3`, ...) 
{
    stopifnot(TRUE)
    n <- min(3, length(x))
    x[seq_len(n)]
}

utils:::head.default() 関数の引数 n = 3 を固定して、コードの余分な部分を取り除いた関数を作ってくれます。
これで関数の動作の見通しがずいぶん良くなりましたね!

n がマイナスのときも見てみましょう。

R
fix_slicing(utils:::head.default(n = -3))
結果
function (x, `n = -3`, ...) 
{
    stopifnot(TRUE)
    n <- max(length(x) + -3, 0L)
    x[seq_len(n)]
}

n がプラスの時とは異なる動作をしていることが分かります。

このように、プログラムから注目したい動作に関係する部分を抽出することを「プログラムスライシング」と言います。2

ぜひ使ってみて下さい!

補足:部分適用との違い

似たようなものに部分適用があります。
これは、関数の一部の引数を固定した、新たな関数を作成します。
R では pryr パッケージの partial() 関数によって可能です。

R
library(pryr)
partial(head, n = 3)
結果
function (...) 
head(n = 3, ...)

これは単に関数の引数を外側から固定するだけであり、関数の内部には無関係です。

fix_slicing() は、関数の内部について、引数を固定した場合にどうなるかを返す関数です。

部分適用の目的は、それによって作られた関数を別のところで呼び出すことにあります。
一方、fix_slicing() の目的は、関数の動作を人間が理解しやすくすることです。

まとめ

fixer パッケージは、R の関数を fix するためのパッケージです。
バグ報告や機能要望などは下記から受け付けています(日本語でOK)。

Enjoy!


  1. 参考: http://akiniwa.hatenablog.jp/entry/2013/12/17/122716 

  2. 入力を固定するのがプログラムスライシングではないです。出力の一部だけに注目して、それだけを出力するようなコードを抽出するのが本流です。