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 の方が短くなる。