Есть ли способ скопировать замыкание в новый объект, который содержит текущее состояние исходного замыкания и при этом быть отделенным для всех предстоящих изменений в среде?
Я пытался найти решение, но при попытке использовать содержащуюся функцию возникла ошибка: Error in { : could not find function "{"
Вот пример кода:
# basic incrementation closure
inc <- function() {
i <- 0
function(x = 1) {
i <<- i + x
i
}
}
copyClosure <- function(x) {
oldEnv <- environment(x)
# set up copy of x in new env
ret <- x
newEnv <- new.env(parent = emptyenv())
environment(ret) <- newEnv
# copy objects from old to new env
objs <- ls(envir = oldEnv, all.names = TRUE, sorted = FALSE)
for (ii in objs) assign(x = ii, envir = newEnv, value = get(x = ii, envir = oldEnv))
return(ret)
}
# original closure object
foo <- inc()
foo(5)
bar <- copyClosure(foo)
# check if objects i are identical
identical(x = get(x = "i", envir = environment(foo)),
y = get(x = "i", envir = environment(bar)))
# check if objects i are separated
foo(5)
identical(x = get(x = "i", envir = environment(foo)),
y = get(x = "i", envir = environment(bar)))
# try using copied closure
bar(5)
В идеале, имея правильную копию, я бы хотел разорвать все связи с любым предком, или я ошибаюсь?
Измените родительскую среду новой среды, чтобы она соответствовала старой среде.
newEnv <- new.env(parent = parent.env(oldEnv))
Тогда кажется, что все работает так, как задумано.
foo <- inc()
foo(5)
# [1] 5
bar <- copyClosure(foo)
identical(x = get(x = "i", envir = environment(foo)),
y = get(x = "i", envir = environment(bar)))
# [1] TRUE
foo(5)
# [1] 10
identical(x = get(x = "i", envir = environment(foo)),
y = get(x = "i", envir = environment(bar)))
# [1] FALSE
bar(5)
# [1] 10
Хорошо, я вижу, что именно на это намекал и @g-grothendieck. Я понимаю, что ссылка на функции теряется при установке родительского объекта на пустойenv(), поскольку поиск источников дальше не будет успешным при отключенном пустом env. Полагаю, если во время копирования изменяется окружающая среда замыкания, в этом подходе нет ничего плохого? Приму ответ, спасибо!
Когда функция
f
запускается, функции, вызываемые в теле этой функции, включая любые базовые функции, такие как{
, просматриваются через предков функцииf
, поэтому, если предком являетсяemptyenv()
, она не сможет найти вообще любые функции.