| Title: | A Tidy API for Sequence Iteration and Set Comprehension |
| Version: | 0.3.1 |
| Description: | A friendly API for sequence iteration and set comprehension. |
| License: | GPL-2 |
| URL: | https://github.com/jacgoldsm/peruse,https://jacgoldsm.github.io/peruse/ |
| BugReports: | https://github.com/jacgoldsm/peruse/issues |
| Encoding: | UTF-8 |
| Imports: | rlang |
| Depends: | magrittr |
| LazyData: | true |
| RoxygenNote: | 7.1.1 |
| Suggests: | testthat, purrr |
| NeedsCompilation: | no |
| Packaged: | 2021-03-08 07:06:02 UTC; storeddocumentsonline |
| Author: | Jacob Goldsmith [aut, cre] |
| Maintainer: | Jacob Goldsmith <jacobg314@hotmail.com> |
| Repository: | CRAN |
| Date/Publication: | 2021-03-08 07:20:02 UTC |
Making an Irregular Sequence Iterator
Description
Create an Iterator object, where the user defines a sequence and aset of initial values, and then callsyield_next() to generate thenext element of the sequence.Iterators are R environments, which meansthey are modified in place, even when passed as arguments to functions.To make a copy of an Iterator that can be modified separately, seeclone().
Usage
Iterator(result, initial, yield)Arguments
result | R expression to run each time 'yield_next' is called |
initial | named list or vector; declare and initialize every variable that appears in 'result' |
yield | variable to yield when 'yield_next()' is called |
Value
An environment object of S3 type Iterator
Note
The expression to be evaluated can include constant values not defined in$initial as long as they are defined in the enclosureof where yield_next() is called,not where the Iterator is created.These values will not vary from iteration toiteration (unless you do something strange in the code, like including<<- in$result.)
See Also
yield_next(),yield_while(),current()rlang::qq_show()
Examples
#Create the Collatz sequence starting with 50 and print out the first 30 elementscollatz <- Iterator({ if (n %% 2 == 0) n <- n / 2 else n <- n*3 + 1 }, initial = c(n = 50), yield = n)seq <- yield_more(collatz, 30)# If you want to define the expression outside the Iterator, use [quote()] and `!!`:expr <- quote(if (n %% 2 == 0) n <- n / 2 else n <- n*3 + 1)collatz <- Iterator(!!expr, c(n = 50), n)# using objects defined outside `$initial`:# Note that `n` in `$initial` overrides the global `n`m <- 100n <- 10it <- Iterator({out <- n + m}, initial = c(n = -10), yield = out)yield_next(it)# environments are modified in place, so be aware:it <- Iterator({m <- m + 1}, c(m = 0), m)other <- ityield_next(it)current(other)clone
Description
Clone an Iterator, making an exact copy that can then be modified separately.This is a simple wrapper aroundrlang::env_clone(). Optionally,override old initial parameters.
Usage
clone(iter, ...)Arguments
iter | an |
... | optionally override the |
Value
a copy of theIterator passed as a parameter
Examples
it <- Iterator({m <- m + n}, list(m = 0, n = 1), m)other <- clone(it)yield_next(it)current(other) == current(it) # falseit2 <- clone(other, n = 5)yield_next(it2)it2$initial$n # 5Get the current value of an Iterator without changing its state
Description
AnIteratoryields a variable every timeyield_next() is called.Get the current value of that variable without changing the state of the Iterator.
Usage
current(iter)Arguments
iter | An |
Value
The current value ofiter
Test if an object is an Iterator
Description
Test if an object is an Iterator
Usage
is_Iterator(list)Arguments
list | Object to test |
Increment an Iterator Without Returning the Value(s)
Description
Increments the Iterator without returning anything.move_more() repeatsmove_next() a specified number of times.move_while()repeatsmove_next() until a condition is met. Refer to the number of the currentiteration with.iter.
Usage
move_next(iter)move_more(iter, more = 1L)move_while(iter, cond)Arguments
iter | An Iterator object object |
more | How many times to iterate |
cond | A quoted logical expression involving some variable(s) in |
Examples
primes <- 2:10000 %>% that_for_all(range(2, .x)) %>% we_have(~.x %% .y != 0, "Iterator")current(primes)move_more(primes, 100)current(primes)Python-style range function
Description
Wrapper aroundbase::seq() that replaces the maximal end value with the supremumand returns an empty vector if b <= a, in the style of Python'srange().Note thatperuse::range views end as a supremum, not a maximum, thus range(a,b)is equivalent to the set[a,b) when a < b or{} when b >= a.
Usage
range(a, b, ...)Arguments
a | minimum |
b | supremum |
... | other params passed to |
See Also
Examples
range(1,5)range(9,10)range(1,6, by = 2)R Set Comprehension
Description
Set comprehension with the magrittr Pipe.Always use the basic syntax:
.x %>% that_for_all(.y) %>% we_have_*(f(.x, .y)),but see the examples for more detail.
Usage
that_for_all(.x, .y)that_for_any(.x, .y)we_have(that_for, formula, result = "vector")Arguments
.x | A set, represented as either an atomic vector or a list |
.y | A set to compare to |
that_for | A list passed to |
formula | A function, lambda, or formula. Must be understood by |
result | Should the expression return a |
Details
formula can be anything that is recognized as a function byrlang::as_function().See the examples for how to specify the end of a sequence when used with anIterator.
Handling missing values in these expressions is possible and sometimes desirable butpotentially painful becauseNA values can't be compared with normal operators.See the README for a detailed example.
Note that.x %>% that_for_all(.y) is vacuously true if.y is empty, while.x %>% that_for_any(.y) is vacuously false if.y is empty.
Value
Forthat_for_all() andthat_for_any(), an object of S3 class that_for_all or that_for_any.Forwe_have(), a vector of the same type as.x ifreturn == 'vector' and an Iterator object ifreturn == 'Iterator'.
Note
if.y is an numeric vector, you probably want a value obtained fromrange(start, end) rather thanstart:end orseq.int(start,end),as when start is greater than end you want an empty vector rather than counting backwards.Note thatrange() views end as a supremum, not a maximum, thusrange(a,b)is equivalent to the set[a,b) when a < b or the empty set when b >= a.
Also note that there is some indirection in the way that.x and.y are referencedin the formula. In the functionwe_have(), the actual name of the two sets is.xand.y. That is what makes the function interface work,e.g.function(.x, .y) .x - .y. On the other hand,purrr-style lambda expressions,e.g.~.x - .y, use positional arguments, where.x is the first argument and.yis the second argument, no matter their names. Because those are actually their names,this difference should never matter.
See Also
The implementation of these functions involves code adapted frompurrr::every()andpurrr::some(), by Lionel Henry, Hadley Wickham, and RStudio, available under theMIT license.
Examples
2:100 %>% that_for_all(range(2, .x)) %>% we_have(function(.x, .y) .x %% .y != 0) #is the same as2:100 %>% that_for_all(range(2, .x)) %>% we_have(~.x %% .y) # 0 = F, (not 0) = T#c.f.primes <- 2:100 %>% that_for_all(range(2, .x)) %>% we_have(~.x %% .y, "Iterator")yield_next(primes)primes2 <- clone(primes)# Refer to the vector .x with `.x_vector` and the current index of that vector with `.i`# For example, to yield to the end of the sequence:yield_while(primes, .x_vector[.i] <= length(.x_vector))# `.finished` is an alias for `.x_vector[.i] > length(.x_vector)`# Equivalent to previous expression:yield_while(primes2, !.finished){c("I", "Don't", "wan't", "chicken") %>% that_for_all("\'") %>% we_have(~grepl(.y, .x))}#Twin primes 1 through 100primes <- 2:100 %>% that_for_all(range(2, .x)) %>% we_have(~.x %% .y)primes %>% that_for_any(primes) %>% we_have(~abs(.x - .y) == 2)#Prime numbers 1 through 100 that are two away from a square number(2:100 %>% that_for_all(range(2, .x)) %>% we_have(~.x %% .y)) %>% that_for_any(range(2, .x)) %>% we_have(~sqrt(.x + 2) == .y | sqrt(.x - 2) == .y)Increment an Iterator and Return the Next Value(s)
Description
Finds the value of the next iteration(s) of an Iterator objectand increments the Iterator to the next value(s).yield_more() repeatsyield_next() a specified number of times.Refer to the number of the current iteration inyield_more() with.iter.
Usage
yield_next(iter)yield_more(iter, more = 1L)Arguments
iter | An Iterator object |
more | How many values to yield |
Value
An object of whatever typeresult evaluates to from the Iterator, ora vector of that type in the case ofyield_more(iter, more > 1L).
Examples
primes <- 2:10000 %>% that_for_all(range(2, .x)) %>% we_have(~.x %% .y != 0, "Iterator")sequence <- yield_more(primes, 100)# use `.iter` to reference the current iterationrwd <- Iterator({ set.seed(seeds[.iter]) n <- n + sample(c(-1L, 1L), size = 1L, prob = c(0.25, 0.75)) }, initial = list(n = 0, seeds = 1:100), yield = n)yield_more(rwd, 100)yield_while
Description
Keep yielding the next element of anIterator while a condition is met.A condition is a logical expression involving variables initer$initial or variablesthat are defined in the enclosure. Refer to the number of the current iteration with.iter.
Usage
yield_while(iter, cond)Arguments
iter | An |
cond | A logical expression involving some variable(s) in |
Examples
collatz <- Iterator({ if (n %% 2 == 0) n <- n / 2 else n <- n*3 + 1 }, initial = list(n = 50), yield = n)yield_while(collatz, n != 1L)p_success <- 0.5threshold <- 100seeds <- 1000:1e6iter <- Iterator({ set.seed(seeds[.iter]) n <- n + sample(c(1,-1), 1, prob = c(p_success, 1 - p_success)) }, list(n = 0), n)sequence <- yield_while(iter, n <= threshold)