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

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/c75214f5/attachment.txt>


[Rd] for loop over S4
Romain Francois