Rの遅延評価
Rの遅延評価そのものはいたって普通ですが、Mapや?applyが副作用を使用しているので変態な動作をします。 from @mickey24
f <- Map(function(x){function() x}, 1:10) f[[1]]() #=> 10 # えっ!?
副作用無しで書けば直観通りに動作します。
car <- function(x) x[1] cdr <- function(x) x[-1] null <- function(x) length(x) == 0 functional.map <- function(fn, ls){ if(null(ls)){ list() }else{ c(fn(car(ls)), functional.map(fn, cdr(ls))) } } side.effect.map <- function(fn, ls){ i <- 0 res <- list() while(i < length(ls)){ i <- i + 1 res[[i]] <- fn(ls[i]) } return(res) } f <- functional.map(function(x){function() x}, 1:10) g <- side.effect.map(function(x){function() x}, 1:10) f[[1]]() #=> 1 g[[1]]() #=> 10
追記 5/7
↓のような理由で↑の現象が起こっているはずです。
ケース A
1 | f <- function(x){ function() x } 2 | a <- 0 3 | g <- f(a) 4 | a <- 1 5 | g() #=> 1
1行目: environment(f) : f = function(x){ function() x } environment(g) : 未生成 2行目: environment(f) : f = function(x){ function() x }, a = 0 environment(g) : 未生成 3行目: Promiseが生成するが、まだ評価されないのでPromiseのまま environment(f) : f = function(x){ function() x }, a = 0 environment(g) : x = <Promise a, environment(f)> 4行目: Promiseはそのまま、environment(f)のaが1に変わってしまう environment(f) : f = function(x){ function() x }, a = 1 environment(g) : x = <Promise a, environment(f)> 5行目: xの値が必要となったのでPromiseが現在のenvironment(f)で評価される environment(f) : f = function(x){ function() x }, a = 1 environment(g) : x = 1
ケース B
1 | f <- function(x){ 1.5 | x; function() x } 2 | a <- 0 3 | g <- f(a) 4 | a <- 1 5 | g() #=> 0
1行目: environment(f) : f = function(x){ x; function() x } environment(g) : 未生成 2行目: environment(f) : f = function(x){ x; function() x }, a = 0 environment(g) : 未生成 3行目 fの1行目:いったんPromiseが生成 environment(f) : f = function(x){ x; function() x }, a = 0 environment(g) : x = <Promise a, environment(f)> 3行目 fの1.5行目: xの値が必要となったので、Promiseが現在のenvironment(f)で評価される environment(f) : f = function(x){ x; function() x }, a = 0 environment(g) : x = 0 4行目: environment(f) : f = function(x){ x; function() x }, a = 1 environment(g) : x = 0 5行目: environment(f) : f = function(x){ x; function() x }, a = 1 environment(g) : x = 0
副作用
function(x){ x; function() x } function(x){ function() x }
上下の式で挙動が異なるので明らかに参照透過性が破られている。lapplyの例では、lapply以外は副作用を用いていない、なのでlapplyが副作用を使用していると言える。lapplyに参照透過性をつけるとメモリや速度が犠牲になるからこの仕様のまま残されているのではないだろうか。