File: recycle.R

package info (click to toggle)
r-cran-itertools 0.1-3-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, forky, sid, trixie
  • size: 316 kB
  • sloc: makefile: 2
file content (131 lines) | stat: -rw-r--r-- 4,056 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#
# Copyright (c) 2009-2010, Stephen B. Weston
#
# This is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
# USA

recycle <- function(iterable, times=NA_integer_) {
  # Manually check for a missing argument since "inherits" issues
  # a cryptic error message in that case
  if (missing(iterable)) {
    stop('argument "iterable" is missing, with no default')
  }

  if (!is.numeric(times) || length(times) != 1 || (!is.na(times) && times < 0)) {
    stop('argument "times" must be a non-negative numeric value')
  }

  times <- as.integer(times)

  if (is.na(times) || times > 1) {
    if (! inherits(iterable, 'iter')) {
      buffer <- iterable
      buffer.iter <- iter(buffer)
    } else {
      iterable.iter <- iter(iterable)
      bsize <- 256  # allocated size of buffer
      bsize.max <- 2 ^ 31 - 1  # maximum allowable allocated size of buffer
      buffer <- vector('list', length=bsize)
      blen <- 0  # number of values currently in buffer
      buffer.iter <- NULL  # will become an iterator over buffer
    }
  } else if (times > 0) {
    iterable.iter <- iter(iterable)
  }

  # This is used until the underlying iterator runs out
  nextEl.buffering <- function() {
    tryCatch({
      # Check if buffer is full
      if (blen >= bsize) {
        # Don't attempt to create a list with more than 2^31-1 elements
        if (blen == bsize.max) {
          stop('underlying iterator has too many values to buffer')
        }
        # Double the size of buffer
        bsize <<- min(2 * bsize, bsize.max)
        length(buffer) <<- bsize
      }
      e <- nextElem(iterable.iter)
      blen <<- blen + 1
      buffer[blen] <<- list(e)
      e
    },
    error=function(e) {
      if (identical(conditionMessage(e), 'StopIteration')) {
        times <<- times - 1L  # will still be greater than zero
        length(buffer) <<- blen
        iterable <<- NULL
        iterable.iter <<- NULL
        buffer.iter <<- iter(buffer)
        nextEl.pointer <<- nextEl.cycling
        nextEl()
      } else {
        stop(e)
      }
    })
  }

  # This will be used once we've run through the underlying iterator
  nextEl.cycling <- function() {
    tryCatch({
      nextElem(buffer.iter)
    },
    error=function(e) {
      if (identical(conditionMessage(e), 'StopIteration')) {
        if (!is.na(times) && times <= 1) {
          times <<- 0L
          stop(e)
        }
        times <<- times - 1L
        buffer.iter <<- iter(buffer)
        # If this throws 'StopIteration', we're done
        nextElem(buffer.iter)
      } else {
        stop(e)
      }
    })
  }

  # This handles the case when "times" is one (pretty useless case)
  nextEl.one <- function() {
    nextElem(iterable.iter)
  }

  # This handles the case when "times" is zero
  nextEl.zero <- function() {
    stop('StopIteration', call.=FALSE)
  }

  # Set the initial value of nextEl.pointer
  if (is.na(times) || times > 1) {
    nextEl.pointer <- if (is.null(buffer.iter)) nextEl.buffering else nextEl.cycling
  } else if (times == 1) {
    nextEl.pointer <- nextEl.one
  } else {
    nextEl.pointer <- nextEl.zero
  }

  # This is the function that will be stored in the iterator object,
  # which will call either nextEl.buffering of nextEl.cycling, depending
  # on the value of nextEl.pointer variable
  nextEl <- function() {
    nextEl.pointer()
  }

  obj <- list(nextElem=nextEl)
  class(obj) <- c('abstractiter', 'iter')
  obj
}