This question refers to "Programming with dplyr"
I want to slice the ... argument of a function and use each element as an argument for a corresponding function.
foo <- function(...){
<some code>
}
should evaluate for example foo(x, y, z) in this form:
list(bar(~x), bar(~y), bar(~z))
so that x, y, z remain quoted till they get evaluated in bar.
I tried this:
foo <- function(...){
arguments <- quos(...)
out <- map(arguments, ~bar(UQ(.)))
out
}
I have two intentions:
Learn better how tidyeval/rlang works and when to use it.
turn future::futureOf() into a function that get me more then one futures at once.
This approach might be overly complicated, because I don't fully understand the underlying concepts of tidyeval yet.
You don't really need any packages for this. match.call can be used.
foo <- function(..., envir = parent.frame()) {
cl <- match.call()
cl$envir <- NULL
cl[[1L]] <- as.name("bar")
lapply(seq_along(cl)[-1], function(i) eval(cl[c(1L, i)], envir))
}
# test
bar <- function(...) match.call()
foo(x = 1, y = 2, z = 3)
giving:
[[1]]
bar(x = 1)
[[2]]
bar(y = 2)
[[3]]
bar(z = 3)
Another test
bar <- function(...) ..1^2
foo(x = 1, y = 2, z = 3)
giving:
[[1]]
[1] 1
[[2]]
[1] 4
[[3]]
[1] 9
Related
In an S3 generic function, I'd like to modify a function argument before calling NextMethod(). As a starting point, I looked through #44 of Henrik Bengtsson's "Wishlist for R". The following snippet is taken from there and corresponds to his suggestion on to how modify an argument before calling NextMethod().
x <- structure(NA, class = "A")
expected <- list(x = x, a = 3)
foo <- function(x, a) UseMethod("foo")
foo.A <- function(x, a) {
a <- a + 1
NextMethod()
}
foo.default <- function(x, a) {
list(x = x, a = a)
}
identical(foo(x, a = 2), expected)
#> [1] TRUE
identical(foo(x, 2), expected)
#> [1] TRUE
Now what has me stumped is the following behavior where the argument to be modified has a default value.
bar <- function(x, a) UseMethod("bar")
bar.A <- function(x, a = 2) {
a <- a + 1
NextMethod()
}
bar.default <- function(x, a = 2) {
list(x = x, a = a)
}
identical(bar(x, a = 2), expected)
#> [1] TRUE
identical(bar(x, 2), expected)
#> [1] TRUE
identical(bar(x), expected)
#> [1] FALSE
Can someone help me understand what is happening here? Any ideas on how to make the default argument case work (apart from an explicit call of bar.default())?
I'm not sure how realistic this set-up is, but the problem with it is that calling bar(x) means that you are calling bar.A(x), then (via NextMethod()) you are calling bar.default(x), rather than bar.default(x, a = 3) as you might expect.
The way round this is to specifically pass a as a parameter in NextMethod. The issue you will have with this is that if the user doesn't name the second parameter, then bar.default will throw because it is being given 3 parameters instead of two (x, 2 and a = 3). You can get round this by including a ... parameter in bar.default so that unnamed parameters are ignored.
x <- structure(NA, class = "A")
expected <- list(x = x, a = 3)
bar <- function(x, ...) UseMethod("bar")
bar.A <- function(x, a = 2) {
a <- a + 1
NextMethod("bar", x, a = a)
}
bar.default <- function(x, ..., a = 2) {
list(x = x, a = a)
}
identical(bar(x, a = 2), expected)
#> [1] TRUE
identical(bar(x, 2), expected)
#> [1] TRUE
identical(bar(x), expected)
#> [1] TRUE
Created on 2020-04-02 by the reprex package (v0.3.0)
I've inherited some code that uses what I think is a common R idiom for libraries, but I'm not sure what is achieved by writing in such a verbose way. Ultimately I intend to re-write but I would first like to know why before I do something stupid.
ecd <-
function(formula, data, subset, weights, offset, ...) {
cl = match.call()
mf = match.call(expand.dots = FALSE)
m =
match(c("formula", "data", "subset", "weights", "offset"),
names(mf),
0L)
mf = mf[c(1L, m)]
mf$drop.unused.levels = TRUE
mf[[1L]] = quote(stats::model.frame)
mf = eval(mf, parent.frame())
mt = attr(mf, "terms")
y = stats::model.response(mf, "numeric")
w = as.vector(stats::model.weights(mf))
offset = as.vector(stats::model.offset(mf))
x = stats::model.matrix(mt, mf, contrasts)
z = ecd.fit(x, y, w, offset, ...)
My current understanding is that it constructs a function call object (type?) from the original arguments to the function and then manually calls it, rather than just calling stats::model.frame directly. Any insights would be appreciated.
I think this should answer everything, explanations are in the code :
# for later
FOO <- function(x) 1000 * x
y <- 1
foo <- function(...) {
cl = match.call()
message("cl")
print(cl)
message("as.list(cl)")
print(as.list(cl))
message("class(cl)")
print(class(cl))
# we can modify the call is if it were a list
cl[[1]] <- quote(FOO)
message("modified call")
print(cl)
y <- 2
# now I want to call it, if I call it here or in the parent.frame might
# give a different output
message("evaluate it locally")
print(eval(cl))
message("evaluate it in the parent environment")
print(eval(cl, parent.frame()))
message("eval.parent is equivalent and more idiomatic")
print(eval.parent(cl))
invisible(NULL)
}
foo(y)
# cl
# foo(y)
# as.list(cl)
# [[1]]
# foo
#
# [[2]]
# y
#
# class(cl)
# [1] "call"
# modified call
# FOO(y)
# evaluate it locally
# [1] 2000
# evaluate it in the parent environment
# [1] 1000
# eval.parent is equivalent and more idiomatic
# [1] 1000
I have three functions and one function is made out of the other two by using useMethod().
logReg <- function(x, ...) UseMethod("logReg")
logRec.numeric <- function(x, y) {
print(x)
}
logReg.formula <- function(formula, data) {
print(formula)
}
My functions are a bit more complex but does not matter for my question. I want logReg to give me additionaly the original function call as output (not the function call of logReg.numeric oder logReg.formula). My first try was:
logReg <- function(x, ...) {
out <- list()
out$call <- match.call()
out
UseMethod("logReg")
}
But it does not work. Can someone give me a hint how to solve my problem?
Here's another way :
logReg <- function(x, ...) {
logReg <- function(x, ...) UseMethod("logReg")
list(logReg(x,...), call=match.call())
}
res <- logReg(1,2)
# [1] 1
res
# [[1]]
# [1] 1
#
# $call
# logReg(x = 1, 2)
#
You can make it work with atttibutes too if you prefer.
Try evaluating it explicitly. Note that this preserves the caller as the parent frame of the method.
logReg <- function(x, ...) {
cl <- mc <- match.call()
cl[[1]] <- as.name("logReg0")
out <- structure(eval.parent(cl), call = mc)
out
}
logReg0 <- function(x, ...) UseMethod("logReg0")
logReg0.numeric <- function(x, ...) print(x)
logReg0.formula <- function(x, ...) print(x)
result <- logReg(c(1,2))
## [1] 1 2
result
## [1] 1 2
## attr(,"call")
## logReg(x = c(1, 2))
I am building a simple R package with many auxiliary functions. One of the main function uses a lot of the auxiliary ones as such:
....
#'# description
#'# param
#'# export
...
mainfunction1 <- function(param1,...,auxiliaryfunction){
# Do some stuff
b <- auxiliaryfunction(param2) + c
return(b)
}
...
#'# description
#'# param
auxiliaryfunction1 <- function(param5,param6,...){# do stuff}
The main function should be used by the final user as such:
result1 <- mainfunction1(param1, param2, auxiliaryfunction1)
The problem is that when the package is built, it never finds the auxiliary functions unless they are exported, however I'd like them not be available to the final user or at least avoid the problem of the user overriding them by mistake by referring to the package namespace.
How can I do this?
Should I export the auxiliary functions too?
You are trying to solve a non-problem.
If you want a user to use a function, export it.
If you don't want a user to use a function, do not export it.
That said...
There is a possibility that you are getting caught up on how functions are passed as arguments to other functions. Functions are first class objects in R, so they can be passed around very easily. Consider the following example:
m <- function(x, y) x + y
n <- function(x, y) x - y
k1 <- function(x, y, FUN) FUN(x, y)
k1(10, 5, FUN = m)
# [1] 15
k1(10, 5, FUN = n)
# [1] 5
k2 <- function(x, y, FUN = m) FUN(x, y)
k2(10, 5) # uses `m()` by default
# [1] 15
k2(10, 5, FUN = m)
# [1] 15
k2(10, 5, FUN = n)
# [1] 5
If you really don't want to users to access the functions directly but want to give them choice over which to use, then define the auxiliary functions in the body of the main function and use, for example, a switch() to choose between them:
fun <- function(x, method = c("A", "B")) {
m <- match.arg(method)
a <- function(x) x^2
b <- function(x) sqrt(x)
switch(m, A = a(x), B = b(x))
}
fun(2)
# [1] 4
fun(2, "A")
# [1] 4
fun(2, "B")
# [1] 1.414214
I'm trying to write a let function that allows me to do things like:
let(a=2, b=3, a+b)
>>> 5
Currently I'm stuck with
let <- function(..., expr) {
with(list(...), quote(expr))
}
which doesn't work at all. Any help appreciated.
Here's one way:
let <- function(..., expr) {
expr <- substitute(expr)
dots <- list(...)
eval(expr, dots)
}
let(a = 2, b = 3, expr = a+b)
# [1] 5
Edit: Alternatively, if you don't want to have to name the expression-to-be-evaluated (i.e. passing it in via expr), and if you are certain that it will always be the last argument, you could do something like this.
let <- function(...) {
args <- as.list(sys.call())[-1]
n <- length(args)
eval(args[[n]], args[-n])
}
let(a = 2, b = 3, a + b)
# [1] 5
let <- function(a,b, expr=a+b){return(expr)}
let(2,3)
# [1] 5