From romain.francois at dbmail.com  Tue Oct 13 15:53:27 2009
From: romain.francois at dbmail.com (Romain Francois)
Date: Tue, 13 Oct 2009 15:53:27 +0200
Subject: [Rd] for loop over S4
In-Reply-To: <4AD483BD.8090804@dbmail.com>
References: <4AD443E1.9080402@dbmail.com> <4AD483BD.8090804@dbmail.com>
Message-ID: <4AD48657.1090008@dbmail.com>


Oops, forgot to add the R code.

On 10/13/2009 03:42 PM, Romain Francois wrote:
> Hello,
>
> Here is a more complete patch implementing an iterating scheme inspired
> from the java Iterable/Iterator design.
>
> Nothing is changed for non S4 objects.
>
>
> The patch contains 4 generic functions :
>
> is.iterable : indicates if an object is iterable. The default method
> returns FALSE
>
> iterator: returns the iterator associated with an iterable object. the
> idea is that this function is only called if the object is iterable.
>
> hasNext: indicates if an iterator has more elements. The default method
> returns FALSE
>
> getNext: returns the next element of the iterator
>
>
>
> Here is an example:
>
>
> require( methods )
>
> setClass( "SimpleIterable", representation( to="integer" ) )
> setClass("SimpleIterator", representation(to="integer", env =
> "environment") )
>
> setMethod("is.iterable", "SimpleIterable", function(x) TRUE )
> setMethod("iterator", "SimpleIterable", function(x){
> env <- new.env()
> assign( "i" , 0L, envir = env )
> new( "SimpleIterator", to = x at to, env = env )
> } )
> setMethod( "hasNext", "SimpleIterator", function(x){
> res <- x at env[["i"]] < x at to
> res
> } )
> setMethod( "getNext", "SimpleIterator", function(x){
> new.i <- x at env[["i"]] + 1L
> assign( "i", new.i, envir = x at env )
> new.i
> } )
>
>
>  > o <- new( "SimpleIterable", to = 10L )
>  > for( i in o ){
> + if( i == 3L ) next
> + if( i == 5L ) break
> + cat( i, "\n" )
> + }
> 1
> 2
> 4
>
>
>
>
> Here is an example iterating over a java Iterable object (the methods
> would need a bit more error trapping) without fetching all the elements
> in advance:
>
> require( rJava )
> .jinit()
>
> setMethod( "is.iterable", "jobjRef", function(x){
> .jinherits( x, "java/lang/Iterable" )
> } )
> setMethod( "iterator", "jobjRef", function(x){
> .jcall( x, "Ljava/util/Iterator;", "iterator" )
> } )
> setMethod( "hasNext", "jobjRef", function(x){
> .jcall( x, "Z", "hasNext" )
> } )
> setMethod( "getNext", "jobjRef", function(x){
> .jcall( x, "Ljava/lang/Object;", "next" )
> } )
>
>  > v <- new( J("java/util/Vector" ) )
>  > v$add( new( J("java/lang/Double" ), 10.2 ) )
> [1] TRUE
>  > v$add( new( J("java/awt/Point"), 10L, 10L ) )
> [1] TRUE
>  > for( i in v){
> + print( i$getClass()$getName() )
> + }
> [1] "java.lang.Double"
> [1] "java.awt.Point"
>
>
> While I'm on this, in the usual for loop :
>
> - why is the switch inside the for. The code would be slighly more
> efficient if it was the other way ?
> - why so many calls to TYPEOF, is it not always going to return the same
> type ?
> - why recreating v each time ?
>
> (I probably miss something here)
>
> Romain
>
> On 10/13/2009 11:09 AM, Romain Francois wrote:
>> Hello,
>>
>> Consider this :
>>
>>> setClass("track", representation(x="numeric", y="numeric"))
>> [1] "track"
>>> o <- new( "track", x = 1, y = 2 )
>>> for( i in o ){
>> + cat( "hello\n")
>> + }
>> Error: invalid type/length (S4/1) in vector allocation
>>
>>
>>
>> This happens at those lines of do_for:
>>
>> n = LENGTH(val);
>> PROTECT_WITH_INDEX(v = allocVector(TYPEOF(val), 1), &vpi);
>>
>> because allocVector( S4SXP, 1) does not make sense.
>>
>>
>>
>> What about detecting S4SXP and attempt to call as.list, similarly to
>> what lapply does ?
>>
>>> as.list.track <- function(x, ...){ list( x = x at x, y = x at y ) }
>>> lapply( o, identity )
>> $x
>> [1] 1
>>
>> $y
>> [1] 2
>>
>>
>> That would make for loops more generic, and make it possible to
>> implement custom "iterators". I'm attaching a patch to eval.c that does
>> just that. For example :
>>
>> > setClass("iterator", representation(to="integer"))
>> [1] "iterator"
>> > o <- new( "iterator", to = 4L )
>> > setGeneric( "as.list" )
>> [1] "as.list"
>> > setMethod( "as.list", "iterator", function(x, ...) {
>> + seq_len( x at to )
>> + })
>> [1] "as.list"
>> >
>> > for( i in o ){
>> + cat( i, "\n" )
>> + }
>> 1
>> 2
>> 3
>> 4
>>
>> Obviously that is the cheap way of doing it, something better would be
>> to abstract a bit more what is an "iterator".
>>
>>
>>
>> For example in java iterators implement just two methods : hasNext()
>> that indicates if there is a next object and next() that returns the
>> next object.
>>
>>
>> For the long story, one motivation for this is actually to deal with
>> java iterators (with the devel version of rJava, and this patch), you
>> might do something like this:
>>
>> > v <- new( J("java/util/Vector") )
>> > v$add( new( J("java/awt/Point"), 10L, 10L ) )
>> [1] TRUE
>> > v$add( new( J("java/lang/Double"), 10 ) )
>> [1] TRUE
>> > for( item in v ){
>> + print( item$getClass()$getName() )
>> + }
>> [1] "java.awt.Point"
>> [1] "java.lang.Double"
>>
>> Where the as.list method for java object references returns a list that
>> is filled by iterating over the object if it implements the Iterable
>> interface.
>>
>> The drawback here is that one has to first fully retrieve the list, by
>> iterating in java, and then process it in R, by iterating again in R.
>>
>> Romain


-- 
Romain Francois
Professional R Enthusiast
+33(0) 6 28 91 30 30
http://romainfrancois.blog.free.fr
|- http://tr.im/BcPw : celebrating R commit #50000
|- http://tr.im/ztCu : RGG #158:161: examples of package IDPmisc
`- http://tr.im/yw8E : New R package : sos

-------------- next part --------------
An embedded and charset-unspecified text was scrubbed...
Name: iterator.txt
URL: <https://stat.ethz.ch/pipermail/r-devel/attachments/20091013/7d97d7cd/attachment.txt>


[Rd] for loop over S4
Romain Francois