[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