I am building a call
in R from a list
with named arguments to the function which elements (amount of elements and names of elements) may vary. The motivation to use a call
is to get a nicer traceback()
in case of an error than with do.call
. Here is an example
set.seed(1)
X <- structure(replicate(3, rnorm(200), simplify = FALSE),
names = c("X1", "X2", "X3"))
str(X)
#R List of 3
#R $ X1: num [1:200] -0.626 0.184 -0.836 1.595 0.33 ...
#R $ X2: num [1:200] 0.409 1.689 1.587 -0.331 -2.285 ...
#R $ X3: num [1:200] 1.074 1.896 -0.603 -0.391 -0.416 ...
func <- function(...){
# ... actual code
if(TRUE) # some error
stop("Boh :(")
}
# `do.call` gives an un-nice traceback
do.call(func, X)
#R Error in func(X1 = X$X1, X2 = X$X2, X3 = X$X3) : Boh :(
traceback()
#R 3: stop("Boh :(") at #3
#R 2: (function (...)
#R {
#R if (TRUE)
#R stop("Boh :(")
#R })(X1 = c(-0.626453810742332, 0.183643324222082, -0.835628612410047,
#R 1.59528080213779, 0.329507771815361, -0.820468384118015, 0.487429052428485,
#R 0.738324705129217, 0.575781351653492, -0.305388387156356, 1.51178116845085,
#R 0.389843236411431, -0.621240580541804, -2.2146998871775, 1.12493091814311,
#R -0.0449336090152309, -0.0161902630989461, 0.943836210685299,
#R 0.821221195098089, 0.593901321217509, 0.918977371608218, 0.782136300731067,
#R 0.0745649833651906, -1.98935169586337, 0.61982574789471, -0.0561287395290008,
#R -0.155795506705329, -1.47075238389927, -0.47815005510862, 0.417941560199702,
#R [output abbreviated. There are many more lines]
# we can build a call instead like
cl <- list(quote(func))
na <- names(X)
cl[na] <- lapply(na, function(z) substitute(X$z, list(z = as.symbol(z))))
(cl <- as.call(cl))
#R func(X1 = X$z, X2 = X$z, X3 = X$z)
# now we get a nice traceback
eval(cl)
#R Error in func(X1 = X$X1, X2 = X$X2, X3 = X$X3) : Boh :(
traceback()
#R 4: stop("Boh :(") at #3
#R 3: func(X1 = X$X1, X2 = X$X2, X3 = X$X3)
#R 2: eval(cl)
#R 1: eval(cl)
What bothers is that the do.call
method can be done in one line where as the call
version takes a few lines. Can this be done smarter while still getting a nice (short and readable) traceback()
? I know I can wrap the call
version into a function so this is not the solution I am looking for.
I know you said you don't want an extra function, but you could keep your code intact and use trace
to use your new function when debugging:
do_call <- function(fun,X){
# remove these comments to use as a simple replacement for do.call
# cl <- list(substitute(fun))
# na <- names(X)
cl[na] <- lapply(na, function(z) substitute(X$z, list(z = as.symbol(z))))
(cl <- as.call(cl))
eval(cl)
}
trace(do.call, quote(return(eval.parent(do_call(substitute(what),substitute(args))))))
do.call(func, X)
#R Error in func(X1 = X$X1, X2 = X$X2, X3 = X$X3) : Boh :(
traceback()
# 12: stop("Boh :(") at #3
# 11: func(X1 = X$X1, X2 = X$X2, X3 = X$X3)
# 10: eval(cl)
# 9: eval(cl) at #6
# 8: do_call(substitute(what), substitute(args))
# 7: eval(expr, p)
# 6: eval.parent(do_call(substitute(what), substitute(args)))
# 5: eval(expr, p)
# 4: eval(expr, p)
# 3: eval.parent(exprObj)
# 2: .doTrace(return(eval.parent(do_call(substitute(what), substitute(args)))),
# "on entry")
# 1: do.call(func, X)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.