25

Is there a package or language construct in R that facilitates or provides the implementation of "Python-like generators"?

By "Python-like generators" I mean functions that keep state between calls, in R syntax and borrowing the keyword yield from Python will be something like:

iterable.fun <- function(){
  yield list('a','b','c')
}

With yield instead of a return, then calling the function three consecutive times would give:

> iterable.fun()
  'a'
> iterable.fun()
  'b'
> iterable.fun()
  'c'

Edit: I left out an aspect of Python generators that makes them different from iterators. It is that the whole list of objects to iterate on is not built on the first call and then iterated, but each function call creates the one element that will return for that call.

papirrin
  • 2,004
  • 22
  • 30
  • 1
    R tries to be a functional language. This request can only be fulfilled by a non-functional approach. You can subvert the functionality with `<<-` if necessary, but it's probably better to think of what you want as an end result, and find a functional solution. – Matthew Lundberg Apr 16 '13 at 03:46
  • Similar question: http://stackoverflow.com/questions/23509381/lazy-sequences-in-r – cbare May 06 '16 at 23:44
  • Also, Luke Tierney wrote up a [Lazy List Implementation](http://homepage.cs.uiowa.edu/~luke/R/lazy/lazy.pdf). – cbare May 06 '16 at 23:49

2 Answers2

31

The iterators package has this functionality

library(iterators)
abc <- iter(c('a','b','c'))
nextElem(abc)
## [1] "a"
nextElem(abc)
## [1] "b"
nextElem(abc)
## [1] "c"

Or you could use lambda.r and <<-. This example is modified from

http://cartesianfaith.wordpress.com/2013/01/05/infinite-generators-in-r/

there are more examples in the blog post

library(lambda.r)
seq.gen(start) %as% {
  value <- start - 1L
  function() {
    value <<- value + 1L
    return(value)
  }
}



foo <- seq.gen(1)
foo()
## [1] 1
foo()
## [1] 2
foo()
## [1] 3

note that you could also use a regular function to do this.

seq.gen <-function(start) {
  value <- start - 1L
  function() {
    value <<- value + 1L
    return(value)
  }
}
foo2 <- seq.gen(1)
foo2()
## [1] 1
foo2()
## [1] 2
foo2()
## [1] 3

If you want to select from a possible list, then you could perhaps do so using switch

seq.char(start) %as% {
  value <- start - 1L
  function() {
    value <<- value + 1L
    return(switch(value,'a','b','c'))
  }
}

foo.char <- seq.char(1)
 foo.char()
## [1] "a"
 foo.char()
## [1] "b"
 foo.char()
## [1] "c"
mnel
  • 113,303
  • 27
  • 265
  • 254
  • 1
    Thanks for your answer, unfortunately an iterator is not what I was talking about and I think I didn't make it clear for those unfamiliar with Python generator functions. – papirrin Apr 16 '13 at 03:59
4

A more recent coro package (coroutines) from the r-lib team offers generators, iterators and adaptive generators. The generator behaves exactly as one would expect (copy-pasting example from the docs):

library(coro)

generate_abc <- generator(function() {
  for (x in letters[1:3]) {
    yield(x)
  }
})
# Create the iterator
abc <- generate_abc()

# Use the iterator by invoking it
abc()
#> [1] "a"

abc()
#> [1] "b"

# Last value
abc()
#> [1] "c"

# Exhaustion sentinel
abc()
#> exhausted

abc()
#> exhausted

See more at https://github.com/r-lib/coro

krassowski
  • 13,598
  • 4
  • 60
  • 92