26

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.

3
  • 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. Commented Apr 16, 2013 at 3:46
  • Similar question: stackoverflow.com/questions/23509381/lazy-sequences-in-r Commented May 6, 2016 at 23:44
  • Also, Luke Tierney wrote up a Lazy List Implementation. Commented May 6, 2016 at 23:49

2 Answers 2

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"
Sign up to request clarification or add additional context in comments.

1 Comment

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.
6

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.