14

次のように関数を作成すると:

what_is_love <- function(f) {
  function(...) {
    cat('f is', f, '\n')
  }
}

そしてそれを呼び出すlapplyfuns <- lapply(c('love', 'cherry'), what_is_love)

予期しない出力が得られます:

> funs[[1]]()
f is cherry
> funs[[2]]()
f is cherry

ただし、使用しない場合はそうではないことに注意してくださいlapply

> f1 <- what_is_love('love')
> f2 <- what_is_love('cherry')
> f1()
f is love
> f2()
f is cherry

何を与える?

funs <- lapply(c('love', 'cherry'), what_is_love)より完全に書き出すことができることを私は知っています:

params <- c('love', 'cherry')
out <- vector('list', length(params))
for (i in seq_along(params)) {
  out[[i]] <- what_is_love(params[[i]])
}
out

しかし、ブラウジングすると、両方の関数に独自の環境があることがわかります。

Browse[1]> out[[1]]
function(...) {
    cat('f is', f, '\n')
  }
<environment: 0x109508478>
Browse[1]> out[[2]]
function(...) {
    cat('f is', f, '\n')
  }
<environment: 0x1094ff750>

しかし、それらの環境のそれぞれでf、同じです...

Browse[1]> environment(out[[1]])$f
[1] "cherry"
Browse[1]> environment(out[[2]])$f
[1] "cherry"

答えは「遅延評価」であることは知っていますが、もう少し深みを探しています...f両方の環境で再割り当てされるのはどうですか? どこfから来たの?この例では、R の遅延評価は内部でどのように機能するのでしょうか?

-

編集:遅延評価と関数に関する他の質問は承知していますが、遅延評価が実際にどのように機能するかを説明せずに、答えは「遅延評価」であると言っているだけです。さらなる深みを求めています。

4

1 に答える 1

17

あなたがするとき

what_is_love <- function(f) {
  function(...) {
    cat('f is', f, '\n')
  }
}

内部関数は のエンクロージャを作成しますが、関数に渡された変数fを実際に使用するまで、それは「約束」のままであり、実際には評価されないという問題があります。の現在の値を「キャプチャ」したい場合fは、promise の評価を強制する必要があります。force()この関数を使用できます。

what_is_love <- function(f) {
  force(f)
  function(...) {
    cat('f is', f, '\n')
  }
}
funs <- lapply(c('love', 'cherry'), what_is_love)

funs[[1]]()
# f is love 
funs[[2]]()
# f is cherry 

がなければforce()fリスト内の両方の関数内の約束のままです。関数を呼び出すまでは評価されず、その関数を呼び出すと、promise はf「cherry」である最後の既知の値に評価されます。

@MartinMorgran が指摘したように、この動作は R 3.2.0 で変更されました。リリースノートより

apply 関数や Reduce() などの高階関数は、クロージャでの遅延評価と変数キャプチャの間の望ましくない相互作用を排除するために、適用する関数に引数を強制するようになりました。これにより、PR#16093 が解決されます。

于 2015-04-19T17:42:25.323 に答える