Why can't we loop over `...`? - r

Why does the following not work?
f = function(...) for (i in ...) print(i)
f(1:3)
# Error in f(1:3) : '...' used in an incorrect context
while this work fine
f = function(...) for (i in 1:length(...)) print(...[i])
f(1:3)
# [1] 1
# [1] 2
# [1] 3

It does not work because the ... object type is not accessible in interpreted code. You need to capture the object as a list as nongkrong showed:
for(i in list(...))
Take a look at the R manual here

Related

Setting equivalent of .subset2 in R?

In R, I can use .subset2 to act as a [[ or $ without dispatch.
> a <- new.env()
> a$foo <- 3
> .subset2(a, "foo")
[1] 3
However, I can't seem to find an equivalent for the setting operation without dispatch:
> .subset2(a, "foo") <- 5
Error in .subset2(a, "foo") <- 5 : could not find function
".subset2<-"
How can I set something without the implicit dispatching of using the [[<- or $<- operators?
You may be looking for assign:
Description: Assign a value to a name in an environment.
assign("foo", 5, envir = a)
By the way, instead of using .subset2 (which is an internal function in the Base Package and can be confused with subset), you might want to use get:
get("foo", envir = a)
# [1] 5
Maybe we can try
with(a,assign("foo",5))
or
with(a, `<-`(foo, 5))
or
a$foo <- 5
I'm not entirely sure this is what you're after, but it does meet the pre-reqs of setting the value for foo in the a environment without explicitly using the $, [, [[, <- operators:
# Your code:
a <- new.env()
a$foo <- 3
.subset2(a, "foo")
# Solution:
list2env(
list(
foo = 5
),
env = a
)
# Proof:
a$foo

R: last argument in elipsis

I have a function f that takes three arguments, and returns the last one. For example:
f <- function(x,y,z){
return(z)
}
f(11,22,33) #yields 33
However, later on I may have more/less than three arguments, so I want to create a function g that returns the last argument of ...
g <- function(...){
#return final argument in '...'
}
g(11,22) #should yield 22
g(11,22,33,44,'abc') #should yield 'abc'
Is there any simple way to do this?
I've looked at existing posts on using ..., but they all seem to use it to pass all the arguments to another function (which is not what I'm trying to do).
I could just make the argument into a vector, and return the last element, but I'd like to avoid that if possible.
Use ...length and ...elt like this:
f <- function(...) ...elt(...length())
f(11, 12, 13)
## [1] 13
g <- function(...) {
dots <- list(...)
if (length(dots)) dots[[length(dots)]]
}
g(11,22)
# [1] 22
g(11,22,33,44,'abc')
# [1] "abc"
g() # returns nothing, NULL invisibly
I often choose to assign to dots or similar in the function and then deal with it, though that can be both good and bad. If you want to pass the args on to other functions, then you can still use ... as before (or you can use do.call(..), though that's not your question). One side-effect is that by doing this, the ... are evaluated, which though generally fine, in some corner-cases this evaluation may be too soon.
A demonstration:
g(stop("quux"), "abc")
# Error in g(stop("quux"), "abc") : quux
If you want to avoid early evaluation of other args, you can use match.call:
g <- function(...){
cl <- match.call(expand.dots = TRUE)
if (length(cl) > 1) cl[[length(cl)]]
}
g() # nothing
g(1)
# [1] 1
g(stop("quux"), "abc")
# [1] "abc"

Use apply function instead of apply for eval parse instead of loop

It's complicated to explain my use case but I am working on a project that requires parsing text that may throw some errors. I would like to use tryCatch() so that as much of the script can run as possible and alert the user that some code failed. I can use a loop for this but I would like to know why this behaviour exists and if there is an apply function that does do the trick.
When I run the loop or use do.call() on this parsed object, I get just the expected single error mesage. When I use lapply() I get the same error message followed by the ouput of the assignments. I've tried throwing suppress functions around lapply() which, perhaps obviously, did not work. and I get similar output for sapply() and map(). Curious if someone can explain it to me.
test_text <- parse(text = "x <- pi; y <- x; z <- stop()")
eval_try <- function(x) {
tryCatch(
eval(x, envir = .GlobalEnv),
error = function(cond) message("there was an error"),
warning = function(cond) message("there was a warning")
)
}
for (i in seq_along(test_text)) {
eval_try(test_text[i])
}
#> there was an error
do.call("eval_try", list(test_text))
#> there was an error
lapply(test_text, eval_try)
# there was an error
# [[1]]
# [1] 3.141593
#
# [[2]]
# [1] 3.141593
#
# [[3]]
# NULL
The printing you see is the output of the lapply function. You can suppress it by assigning the result to a variable or if you really don't care about storing the output, use the below trick with invisible.
> myfun <- function(x) x
>
> lapply(1:3, FUN = myfun)
[[1]]
[1] 1
[[2]]
[1] 2
[[3]]
[1] 3
> a <- lapply(1:3, FUN = myfun)
> invisible(lapply(1:3, FUN = myfun))

How can I add additional arguments to methods for internal generics?

I want to implement an inset method for my class myClass for the internal generic [<- (~ help(Extract)).
This method should run a bunch of tests, before passing on the actual insetting off to [<- via NextMethod().
I understand that:
any method has to include at least the arguments of the generic (mine does, I think)
the NextMethod() call does not usually need any arguments (though supplying them manually doesn't seem to help either).
Here's my reprex:
x <- c(1,2)
class(x) <- c("myClass", "numeric")
`[<-.myClass` <- function(x, i, j, value, foo = TRUE, ...) {
if (foo) {
stop("'foo' must be false!")
}
NextMethod()
}
x[1] <- 3 # this errors out with *expected* error message, so dispatch works
x[1, foo = FALSE] <- 3 # this fails with "incorrect number of subscripts
What seems to be happening is that NextMethod() also passes on foo to the internal generic [<-, which mistakes foo for another index, and, consequently errors out (because, in this case, x has no second dimension to index on).
I also tried supplying the arguments explicitly no NextMethod(), but this also fails (see reprex below the break).
How can I avoid choking up NextMethod() with additional arguments to my method?
(Bonus: Does anyone know good resources for building methods for internal generics? #Hadleys adv-r is a bit short on the matter).
Reprex with explicit arguments:
x <- c(1,2)
class(x) <- c("myClass", "numeric")
`[<-.myClass` <- function(x, i = NULL, j = NULL, value, foo = TRUE, ...) {
if (foo) {
stop("'foo' must be false!")
}
NextMethod(generic = "`[<-`", object = x, i = i, j = j, value = value, ...)
}
x[1] <- 3 # this errors out with expected error message, so dispatch works
x[1, foo = FALSE] <- 3 # this fails with "incorrect number of subscripts
I don't see an easy way around this except to strip the class (which makes a copy of x)
`[<-.myClass` <- function(x, i, value, ..., foo = TRUE) {
if (foo) {
cat("hi!")
x
} else {
class_x <- class(x)
x <- unclass(x)
x[i] <- value
class(x) <- class_x
x
}
}
x <- structure(1:2, class = "myClass")
x[1] <- 3
#> hi!
x[1, foo = FALSE] <- 3
x
#> [1] 3 2
#> attr(,"class")
#> [1] "myClass"
This is not a general approach - it's only needed for [, [<-, etc because they don't use the regular rules for argument matching:
Note that these operations do not match their index arguments in the standard way: argument names are ignored and positional matching only is used. So m[j = 2, i = 1] is equivalent to m[2, 1] and not to m[1, 2].
(from the "Argument matching" section in ?`[`)
That means your x[1, foo = FALSE] is equivalent to x[1, FALSE] and then you get an error message because x is not a matrix.
Approaches that don't work:
Supplying additional arguments to NextMethod(): this can only increase the number of arguments, not decrease it
Unbinding foo with rm(foo): this leads to an error about undefined foo.
Replacing foo with a missing symbol: this leads to an error that foo is not supplied with no default argument.
Here's how I understand it, but I don't know so much about that subject so I hope I don't say too many wrong things.
From ?NextMethod
NextMethod invokes the next method (determined by the class vector,
either of the object supplied to the generic, or of the first argument
to the function containing NextMethod if a method was invoked
directly).
Your class vector is :
x <- c(1,2)
class(x) <- "myClass" # note: you might want class(x) <- c("myClass", class(x))
class(x) # [1] "myClass"
So you have no "next method" here, and [<-.default, doesn't exist.
What would happen if we define it ?
`[<-.default` <- function(x, i, j, value, ...) {print("default"); value}
x[1, foo = FALSE] <- 3
# [1] "default"
x
# [1] 3
If there was a default method with a ... argument it would work fine as the foo argument would go there, but it's not the case so I believe NextMethod just cannot be called as is.
You could do the following to hack around the fact that whatever is called doesn't like to be fed a foo argument:
`[<-.myClass` <- function(x, i, j, value, foo = FALSE, ...) {
if (foo) {
stop("'foo' must be false!")
}
`[<-.myClass` <- function(x, i, j, value, ...) NextMethod()
args <- as.list(match.call())[-1]
args <- args[names(args) %in% c("","x","i","j","value")]
do.call("[<-",args)
}
x[1, foo = FALSE] <- 3
x
# [1] 3 2
# attr(,"class")
# [1] "myClass"
Another example, with a more complex class :
library(data.table)
x <- as.data.table(iris[1:2,1:2])
class(x) <- c("myClass",class(x))
x[1, 2, foo = FALSE] <- 9999
# Sepal.Length Sepal.Width
# 1: 5.1 9999
# 2: 4.9 3
class(x)
# [1] "myClass" "data.table" "data.frame"
This would fail if the next method had other arguments than x, i, j and value, in that case better to be explicit about our additional arguments and run args <- args[! names(args) %in% c("foo","bar")]. Then it might work (as long as arguments are given explicitly as match.call doesn't catch default arguments). I couldn't test this though as I don't know such method for [<-.

R - Evaluate a nested function in an environment

I am trying to run a chunk of R code in a sandbox-ed fashion, by loading all the necessary dependencies (functions and data) into a new environment and evaluating an expression within that environment. However, I'm running into trouble with functions calling other functions in the environment. Here's a simple example:
jobenv <- new.env(parent=globalenv())
assign("f1", function(x) x*2, envir=jobenv)
assign("f2", function(y) f1(y) + 1, envir=jobenv)
expr <- quote(f2(3))
Using eval on expr fails since f2 can't find f1
> eval(expr, envir=jobenv)
Error in f2(3) : could not find function "f1"
whereas explicitly attaching the environment works
> attach(jobenv)
> eval(expr)
[1] 7
I'm probably missing something obvious, but I couldn't find any permutation of the eval call that works. Is there a way to get the same effect without attaching the environment?
There are a number of ways of doing this, but I kind of like this one:
jobenv <- new.env(parent=globalenv())
local({
f1 <- function(x) x*2
f2 <- function(y) f1(y) + 1
}, envir=jobenv)
## Check that it works
ls(jobenv)
# [1] "f1" "f2"
local(f2(3), envir=jobenv)
# [1] 7
eval(quote(f2(3)), envir=jobenv)
# [1] 7
Scope is defined when the function is created, not when it's called. See section 10.7 of the Introduction to R manual.
This seems a bit odd to me, but you get the same behavior even if you avoid assign all together and just use $<-.
jobenv <- new.env(parent=globalenv())
jobenv$f1 <- function(x) x*2
jobenv$f2 <- function(y) f1(y) + 1
expr <- quote(f2(3))
eval(expr, envir=jobenv)
This seems to be because the enclosing environment of f1 and f2 is the global environment. I would have expected it to be jobenv.
> environment(jobenv$f1)
<environment: R_GlobalEnv>
> environment(jobenv$f2)
<environment: R_GlobalEnv>
One solution is to explicitly set the environment of each function... but there has to be an easier way.
> environment(jobenv$f1) <- jobenv
> environment(jobenv$f2) <- jobenv
> eval(expr, envir=jobenv)
[1] 7

Resources