Closed nverno closed 8 years ago
I don't think so - I believe that with R's reference counting, if there's only one reference to a list, then when you set an element in the list, it modifies the list directly, without making a copy.
Also, one danger of using the code you have there is if someone makes a copy of the list with x <- example$example
, then modifying one will modify the other, which would be confusing.
I had thought it wouldn't make a copy of the list as well, but when experimenting with the above example using a regular setter function, ie, using simply
set_example = function(index, value) {
self$example[[index]] <- value
and tracing the memory of example$example
it seemed to be copying every time a value was set. I has some hazy idea that the reference counter could be incremented when values are passed to non-primitive functions. I agree about the confusing part in your danger warning -- I'll just close it unless there is interest.
Interesting... I suspect that if you used R6Class(portable=FALSE)
, and then set it with example[[index]] <<- value
, it won't make a copy. Using the form self$example[[index]] <- value
may cause a copy to be made. If you test that out, can you report back what you find?
I tried with portable=FALSE
, and using <<-
in place of both <-
in the initialize
and set_example
functions, but it still appears to me that a copy is being created (using tracemem
). Ill just dump the whole thing here, in case I'm making a silly mistake,
Example2 <- R6Class(
"Example2",
public = list(
example = NA,
initialize = function(length) {
if (!missing(length)) example <<- vector("list", length)
},
set_example = function(index, value) example[[index]] <<- value
),
portable = FALSE
)
example <- Example2$new(length=10)
tracemem(example$example)
example$set_example(1, "B")
example$example[[1]]
Hopefully, I will get some more time over the next couple of days to look deeper into it, and maybe be able to report back a fuller description of what is going on. I love the package btw, and hope to be using it quite a bit.
I think I have an idea of why this is happening. I've modified the code to move the tracemem()
call inside of the initialize
method. With this version of the code, it does modify example$example
in place.
Example3 <- R6Class(
"Example3",
public = list(
example = NA,
initialize = function(length) {
if (!missing(length)) example <<- vector("list", length)
print(tracemem(example))
},
set_example = function(index, value) example[[index]] <<- value
),
portable = FALSE
)
example <- Example3$new(length=10)
example$set_example(1, "B")
example$example[[1]]
# Check that the address hasn't changed
.Internal(inspect(example$example))
I believe it's because in Example2, there's the line tracemem(example$example)
-- simply accessing example$example
results in a copy of the object. I don't know that much about how the ref count mechanism works, but if it's involved here, I can only guess that it's not getting decremented after the tracemem(example$example)
.
Note also that when running test code like this, you may need to run the entire code as one block, instead of line-by-line, because R's automatic assignment of .Last.value
can create another copy of an object.
One more version... this version of the code is similar to Example2
, but with assignment using self$example[[index]] <- value
. The first time a value is set, it triggers tracemem
, but subsequent settings do not do so, until a value from example$example
is accessed.
Example4 <- R6Class(
"Example4",
public = list(
example = NA,
tracemem = tracemem,
initialize = function(length) {
if (!missing(length)) self$example <- vector("list", length)
},
set_example = function(index, value) self$example[[index]] <- value
)
)
example <- Example4$new(length=10)
tracemem(example$example)
example$set_example(1, "B") # Triggers tracemem
example$set_example(1, "C") # Does NOT trigger tracemem
example$set_example(1, "D") # Does NOT trigger tracemem
# Access a value
example$example[[1]]
example$set_example(1, "E") # Triggers tracemem
Would it make sense to have setter functions change fields by reference? Something like the following example,