[R] creating list with 200 identical objects

Wacek Kusnierczyk Waclaw.Marcin.Kusnierczyk at idi.ntnu.no
Mon Jun 1 23:16:17 CEST 2009


Romain Francois wrote:
>
> rep( list( new("track") ), 5 )
> lapply( 1:5, function(x) new("track") )
> list( new("track") ) [ rep(1, 5 ) ]

but note: these are not all equivalent.  the second creates a list of
five distinct objects, the first and last ones create a list of five
times the same object.  depending on how the class is implemented and on
the assumptions your subsequent code makes, it may be essential or
negligible.

consider:

    setClass('foo',
        representation=representation(content='character'),
        prototype=list(content='foo'))

    foos = lapply(1:2, function(x) new('foo'))
    foos[[1]]@content
    # 'foo'
    foos[[2]]@content
    # 'foo'
    foos[[1]]@content = 'bar'
    foos[[2]]@content
    # 'foo'

obviously, because you have two independently created objects.  but:
  
    foos = rep(list(new('foo')), 2)
    foos[[1]]@content = 'bar'
    foos[[2]]@content
    # 'foo'

even though new is called just once;  this is because r sometimes
pretends to be functional, and makes a copy on assignment. 

consider:

    setClass('foo',
        representation(content='character'),
        prototype=list(content='foo'))
    setMethod('initialize', 'foo', local({
        count = 0
        function(.Object) {
            count <<- count + 1
            cat(sprintf("making foo #%d\n", count))
            .Object } }))

    foos = lapply(1:2, function(x) new('foo'))
    # initialize called two times

    foos = rep(list(new('foo')), 2)
    # initalize called just once
    foos[[1]]@content = 'bar'
    # initialize not called at all
    foos[1:2]

looks like a zombie -- a list sort of containing the same object twice
(note, 'new', and indirectly 'initialize', have been called just once),
yet sort of two distinct objects (note, they have different content
values). see more remarks below.

consider:

    setClass('foo',
       representation=representation(content='environment'))
    setMethod('initialize', 'foo', function(.Object) {
        .Object at content = new.env()
        .Object at content$name = 'foo'
        .Object })

    foos = rep(list(new('foo')), 2)
    foos[[1]]@content$name = 'bar'
    foos[[2]]@content$name
    # 'bar'

of course, because you have just one object twice on the list, its
content is an environment, and with environments r does not pretend to
be functional.  but:

    foos = lapply(1:2, function(x) new('foo'))
    foos[[1]]@content$name = 'bar'
    foos[[2]]@content$name
    # 'foo'
  
of course, because you have two distinct objects, and assignment to one
of them has no effect on the other.  similarly if 'foo' is defined as
follows:

    setClass('foo',
       representation=representation(content='environment'))
    setMethod('initialize', 'foo', function(.Object) {
       .Object at content$name = 'foo'
       .Object })

or as follows:

    setClass('foo',
       representation=representation(content='environment'),
       prototype=list(content={
           e=new.env()
           e$name='foo'
           e }))

note that the solution hinted by chuck cleland (modified):

    foos = vector('list', 2)
    for (i in 1:2)
       foos[[i]] = new('foo')

will populate the list with distinct objects, which seems to be what you
wanted.

    setClass('foo', representation(id='integer', name='character'))
    setMethod('initialize', 'foo', local({ count = 0L; function
(.Object, name) {
       count <<- count + 1L
       .Object$id = count
       .Object$name = name
       .Object } }))


back to the zombie case:  the fact that you can obtain a new instance of
a class without 'new' (and the respective 'initialize') to be called
seems a design flaw or a bug in the s4 object system.  furthermore,
according to [1, p. 339+],

"Once a non-virtual class has been created, objects from that class can
be generated by a call to the function new(), with the name of the class
as the first argument. If that is the only argument, new() returns the
prototype object from the class. If other arguments are supplied, new()
passes the prototype object and those arguments on to the function
initialize()."

but here, 'new' is called with just the class name (i.e., as
"new('foo')"), yet 'initialize' is triggered, too.  a bug?

on the side, note that there have actually been *two* instances of foo
created [2] by the call to rep: one when new was called, and another
from the subsequent call to initialize, as it creates and returns a
*new* instance of 'foo' (without 'new' being called for that purpose!). 
but from your perspective, you get this one object on the list, twice.


vQ

[1] 'Software for Data Analysis -- Programming with R', Chambers,
Springer 2008

[2] 'S4 classes in 15 pages, more or less' (actually, 12 pages +
references), www.stat.auckland.ac.nz/S-Workshop/Gentleman/S4Objects.pdf




More information about the R-help mailing list