R で無限長ベクトル

http://www.ibm.com/developerworks/jp/linux/library/l-r3/index.html
の無限シーケンスをまねてやってみる。

# 初期値と数列を生成する関数を渡して、無限数列生成の関数を作る
make_infinity <- function(init, func){
    inf_vec <- init
    function(idx, len) {
        extend_by <- max(idx + len - 1 - length(inf_vec), 0)
        inf_vec <<- c(inf_vec, func(idx=length(inf_vec)+1, len=extend_by))
        inf_vec[seq.int(idx, length.out=len)]
    }
}

# 数列を生成する関数は、ある位置 idx から長さ len の数列を生成
fib <- function(idx, len){
    init <- c(-2,-1)
	# 以前の値を利用するときは、上位フレームからとってくる
	vec <- get("inf_vec", parent.frame(1))[idx + init]
    for(i in seq.int(3, length.out=len)) {
        vec <- c(vec, vec[i-2] + vec[i-1])
    }
    vec[init]
}

inf_fib <- make_infinity(c(1,1), fib)

inf_fib(3, 10)
# [1]   2   3   5   8  13  21  34  55  89 144

漸化式ならともかく、ある idx の値を生成できる関数があるなら、

make_infinity_lite <- function(func) {
    inf_vec <- numeric(0)
    function(idx) {
        extend_idx <- idx[which(is.na(inf_vec[idx]))]
        inf_vec[extend_idx] <<- func(extend_idx)
        inf_vec[idx]
    }
}
inf_sin <- make_infinity_lite(sin)

inf_sin(c(3,5,10))
# [1]  0.1411200 -0.9589243 -0.5440211

でよさそうやな。
この方が、必要な分しか計算しないから軽そう。


ついでに、漸化式で環境を使ってみたバージョン
関数が「参照渡し」っぽく動作する。

make_infinity_env <- function(init, func){
    inf_vec <- init
    e <- environment()
    function(idx, len) {
        extend_by <- max(idx + len - 1 - length(inf_vec), 0)
        func(idx=length(inf_vec)+1, len=extend_by, env=e)
        inf_vec[seq.int(idx, length.out=len)]
    }
}

fib_env1 <- function(idx, len, env){
    for(i in seq.int(idx, length.out=len)) {
		# i の値を env の環境で使えるように割り当て(
        assign("i_",i, env)
        evalq(inf_vec <- c(inf_vec, inf_vec[i_-2] + inf_vec[i_-1]), env)
    }
	# 必要なくなったオブジェクトの削除
    rm(i_, envir=env)
}

fib_env2 <- function(idx, len, env){
	# 環境のチェーンを付け替え
    c_env <- environment()
    parent.env(c_env) <- parent.env(env)
    parent.env(env) <- c_env
    for(i_ in seq.int(idx, length.out=len)) {
        evalq(inf_vec <- c(inf_vec, inf_vec[i_-2] + inf_vec[i_-1]), env)
    }
	# 付け替えた環境をもとに戻す
    parent.env(env) <- parent.env(c_env)
}

inf_fib_env1 <- make_infinity_env(c(1,1), fib_env1)
inf_fib_env(5, 10)
#[1]   5   8  13  21  34  55  89 144 233 377
inf_fib_env2 <- make_infinity_env(c(1,1), fib_env2)
inf_fib_env(4, 10)
#[1]   3   5   8  13  21  34  55  89 144 233

実際は、inf_vec の存在する環境で eval してるだけ。
fib_envのオブジェクトで eval 時にいるもの(ここでは i_)が少ないときは、fib_env1の方が短くてすむ。けど、多くなってくると fib_env2 の方が短くなる。