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>