[Rd] Strange R object

Simon Urbanek simon.urbanek at r-project.org
Fri Jul 9 19:02:40 CEST 2010


On Jul 9, 2010, at 12:41 PM, Deepayan Sarkar wrote:

> On Fri, Jul 9, 2010 at 5:25 AM, Peter Dalgaard <pdalgd at gmail.com> wrote:
>> Gabor Grothendieck wrote:
>>> On Fri, Jul 9, 2010 at 5:09 AM, Peter Dalgaard <pdalgd at gmail.com> wrote:
>>>> Gabor Grothendieck wrote:
>>>>> I have *** attached *** an RData file containing an R object that
>>>>> is acting strangely.
>>>>> 
>>>>> Try this in a fresh workspace. Do not load zoo or any other package.
>>>>> We load the object, zz2, from the attached RData file.  It is just
>>>>> the number 1 with the class c("zooreg", "zoo").
>>>>> 
>>>>> Now create an S3 print routine that simply prints an X when given
>>>>> an object of class "zoo".
>>>>> 
>>>>> If we use print on the object it produces an X but not if we just
>>>>> enter it at the console.  Also the object is not identical to its
>>>>> dput output.
>>>>> 
>>>>> How can such an object exist?  What is it about the object that is
>>>>> different from structure(1, class = c("zoo", "zooreg")) ?
>>>>> 
>>>> There's a bit in the SEXP structure that is supposed to be turned on
>>>> when an object has an S3 class. This is where implicit print looks,
>>>> whereas explicit print looks, er, elsewhere. Notice that
>>>> 
>>>>> is.object(zz2)
>>>> [1] FALSE
>>>>> class(zz2) <- class(zz2)
>>>>> zz2
>>>> X
>>>>> is.object(zz2)
>>>> [1] TRUE
>>>> 
>>>> Whenever the same information is stored in two ways, there is a risk of
>>>> inconsistency, so it is not too strange that you can have an ill-formed
>>>> .Rdata file (if you save zz2 back out, after the above fixup, line 11
>>>> changes from 526 to 782, corresponding to the bit being turned on).
>>>> 
>>>> I don't think it is the job of load() to verify object structures, since
>>>> there is no end to that task. Rather, we shouldn't create them in the
>>>> first place, but you give us no clues as to how that object got made.
>>>> 
>>> 
>>> This was originally a large object in a program that uses a variety of
>>> packages and it took quite a long time just to narrow it down to the
>>> point where I had an object sufficiently small to post.  Its not even
>>> clear at what point the object goes bad but your class(x) <- class(x)
>>> trick helped a lot and I have now been able to recreate it in a simple
>>> manner.
>>> 
>>> Below we create a new S3 class "X" with an Ops.X and print.X method.
>>> We then create an object x of that class which is just 1 with a class
>>> of "X".  When we multiply 1*x we get the bad object.  1*x and x have
>>> the same dput output but compare as FALSE.  1*x is not printed by
>>> print.X even though it is of class "X" while x is printed by print.X .
>>>  If we assign 1*x to xx and use your class assignment trick (class(xx)
>>> <- class(xx)) then xx prints as expected even though it did not prior
>>> to the class assignment.
>>> 
>>>> Ops.X <- function(e1, e2) { print("Ops.X"); NextMethod(.Generic) }
>>>> print.X <- function(x, ...) print("print.X")
>>>> x <- structure(1, class = "X")
>>>> dput(x)
>>> structure(1, class = "X")
>>>> dput(1*x)
>>> [1] "Ops.X"
>>> structure(1, class = "X")
>>>> identical(x, 1*x)
>>> [1] "Ops.X"
>>> [1] FALSE
>>>> 1*x
>>> [1] "Ops.X"
>>> [1] 1
>>> attr(,"class")
>>> [1] "X"
>>>> x
>>> [1] "print.X"
>>>> xx <- 1*x
>>> [1] "Ops.X"
>>>> class(xx) <- class(xx)
>>>> xx
>>> [1] "print.X"
>> 
>> Or, to minimize it further:
>> 
>>> x <- structure(1, class="y")
>>> is.object(x)
>> [1] TRUE
>>> is.object(x*1)
>> [1] TRUE
>>> is.object(1*x)
>> [1] FALSE
>>> class(x*1)
>> [1] "y"
>>> class(1*x)
>> [1] "y"
>> 
>> Yup, that looks like a bug.
> 
> I recently came across the following surprising behaviour which turns
> out to be the same issue. I had been meaning to ask for an
> explanation.
> 
>> x <- 1:20
>> class(x)
> [1] "integer"
>> is.object(x)
> [1] FALSE
>> print.integer <- function(x) print(x %% 5)
>> print(x)
> [1] 1 2 3 4 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 0
>> x
> [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20
> 


... that is an entirely different issue. x is still not an object because it doesn't have any explicit S3 class so it has nothing in common with the case discussed. This is about P in REPL which uses PrintValueEnv which is turn dispatches to print() only for objects (see main and print).

Cheers,
Simon



More information about the R-devel mailing list