match.call with default arguments
Hopefully, this doesn't lead to dragons.
foo <- function(x=NULL,y=NULL,z=2) {
mget(names(formals()),sys.frame(sys.nframe()))
}
foo(x=4)
$x
[1] 4
$y
NULL
$z
[1] 2
print(foo(x=4))
$x
[1] 4
$y
NULL
$z
[1] 2
Setting default arguments to `...`
I think this one calls for do.call()
. Notice I have renamed your default arguments x
and y
to match those for arguments 1 and 2 in plot()
. If you want to continue to use a
and b
, uncomment the fourth line. But it will make things a lot easier if you stick with the default names.
simpleFun <- function(x, y, ...) {
args <- as.list(match.call()[-1])
if(!any(names(args) == "xlim"))
args$xlim <- c(-6, 6)
## names(args)[1:2] <- c("x", "y")
do.call("plot", args)
}
This seems to work fine on these sample runs.
simpleFun2(a = rnorm(10), b = rnorm(10))
simpleFun2(a = rnorm(10), b = rnorm(10), xlim = c(-1, 1))
simpleFun2(a = rnorm(10), b = rnorm(10), xlim = c(-1, 1), ylim = c(-4, 4))
Call function with dots arguments and default value in another function with dots arguments and default value
@Onyambu replied in the comments. But I want to put his answer here hoping that it can help someone.
f<- function(x, y, z = 3, ...){
x*y*z
}
g <- function(w = 4, ...){
w*f(...)
}
g(w = 2, x = 1, y = 5)
[1] 30
Default argument depending of the function matched in R
1) Try redefining table and xtabs in newfun. Ensure that fun is calling the local versions by converting it to character and using do.call.
newfun <- function(..., fun) {
table <- function(x, ..., useNA = "ifany") base::table(x, ..., useNA = useNA)
xtabs <- function(x, ..., na.action = NULL, addNA = NULL)
stats::xtabs(x, ..., na.action = na.action, addNA = addNA)
fun <- deparse(substitute(fun))
do.call(fun, list(...))
}
newfun(warpbreaks[-1], fun = table)
newfun(breaks ~ ., warpbreaks, fun = xtabs)
2) Another approach is to have 3 functions, one for your version of table, one for your version of xtabs and then one to contain the common code which each of the others would call. That may be more straight forward than (1).
mytable <- function(..., useNA = "ifany") {
tab <- table(..., useNA = useNA)
other(tab)
tab
}
myxtabs <- function(..., na.action = NULL, addNA = TRUE) {
tab <- xtabs(..., na.action = na.action, addNA = addNA)
other(tab)
tab
}
other <- function(x) {
# code
}
How to explicitly call the default value of a function argument in R?
You can access the argument list and default values via:
> formals(rnorm)
$n
$mean
[1] 0
$sd
[1] 1
formals("rnorm")
also works. Some simple examples:
> rnorm(10,mean = formals(rnorm)$mean)
[1] -0.5376897 0.4372421 0.3449424 -0.9569394 -1.1459726 -0.6109554 0.1907090 0.2991381 -0.2713715
[10] -1.4462570
> rnorm(10,mean = formals(rnorm)$mean + 3)
[1] 2.701544 2.863189 1.709289 2.987687 2.848045 5.136735 2.559616 3.827967 3.079658 5.016970
Obviously, you could store the result of formals(rnorm)
ahead of time as well.
getting the arguments of a parent function in R, with names
get_args <- function()
{
cl <- sys.call(-1)
f <- get(as.character(cl[[1]]), mode="function", sys.frame(-2))
cl <- match.call(definition=f, call=cl)
as.list(cl)[-1]
}
The key here is to set the definition
argument to match.call
to be get_arg
's calling function. This should (hopefully!) work for the general case where get_args
can be called from anywhere.
Compilation error when calling function with default arguments
That's not how default arguments work. The default argument has to go in the declaration, not the definition:
// foo.h
void foo(int, int, int = 5); // default values here
// foo.cpp
void foo(int a, int b, int c)
{
// ...
}
Think about it: Every TU that wants to use the function has to know the default value. This only makes sense in the declaration, which every user of the function must see.
Why is match.call useful?
One reason that is relevant here is that match.call
captures the language of the call without evaluating it, and in this case it allows lm
to treat some of the "missing" variables as "optional". Consider:
lm(x ~ y, data.frame(x=1:10, y=runif(10)))
Vs:
lm2 <- function (
formula, data, subset, weights, na.action, method = "qr",
model = TRUE, x = FALSE, y = FALSE, qr = TRUE, singular.ok = TRUE,
contrasts = NULL, offset, ...
) {
mf <- model.frame(
formula = formula, data = data, subset = subset, weights = weights
)
}
lm2(x ~ y, data.frame(x=1:10, y=runif(10)))
## Error in model.frame.default(formula = formula, data = data, subset = subset, :
## invalid type (closure) for variable '(weights)'
In lm2
, since weights
is "missing" but you still use it in weights=weights
, R tries to use the stats::weights
function which is clearly not what was intended. You could get around this by testing for missingness before you call model.frame
, but at that point the match.call
starts looking pretty good. Look at what happens if we debug
the call:
debug(lm2)
lm2(x ~ y, data.frame(x=1:10, y=runif(10)))
## debugging in: lm2(x ~ y, data.frame(x = 1:10, y = runif(10)))
## debug at #5: {
## mf <- model.frame(formula = formula, data = data, subset = subset,
## weights = weights)
## }
Browse[2]> match.call()
## lm2(formula = x ~ y, data = data.frame(x = 1:10, y = runif(10)))
match.call
doesn't involve the missing arguments at all.
You could argue that the optional arguments should have been made explicitly optional via default values, but that's not what happened here.
Related Topics
How to Loop Through a Folder of CSV Files in R
If_Else() 'False' Must Be Type Double, Not Integer - in R
Make R Studio Plots Only Show Up in New Window
Set Environment Variables for System() in R
How to Loop Over the Length of a Dataframe in R
How to Increase the Space Between Grouped Bars in Ggplot2
How to Skip Error Checking at Rmarkdown Compiling
How to Preprocess Features When Some of Them Are Factors
Sum by Distinct Column Value in R
Scale_Y_Log10() and Coord_Trans(Ytrans = 'Log10') Lead to Different Results
How to Remove Groups of Observation with Dplyr::Filter()
Function Commenting Conventions in R
How to Do a Data.Table Rolling Join
Creating a Monthly/Yearly Calendar Image with Ggplot2
How Does One Merge Dataframes by Row Name Without Adding a "Row.Names" Column
Sum Multiple Columns by Group with Tapply