rnorouzian
rnorouzian

Reputation: 7517

How to find out what argument values have been used in an R function call?

I was wondering if there is an Base R function to extract the argument values that are in use in a particular function call?

For example, for each of the objects x, y and z below, is there a generic way to extract the arguments names (e.g., n, sd, rate, scale) in use and the values (e.g., 1e4 for n) assigned by the user or the system to each of those arguments?

NOTE: in certain "R" functions such extraction is easily done. For example, in density(), one can easily extract the argument values using density()$call.

 x = rnorm(n = 1e4, mean = 2, sd = 4)
 y = rlogis(1e4, 20)
 z = rexp(1e4, 5)

Upvotes: 2

Views: 720

Answers (1)

alistaire
alistaire

Reputation: 43334

This is really not an easy thing to do. If you're building the function, you can capture the call with match.call, which can be parsed without too much trouble:

f <- function(x, y = 1, ...){
    cl <- match.call()
    as.list(cl[-1])
}

str(f(1))
#> List of 1
#>  $ x: num 1

str(f(1, 'foo'))
#> List of 2
#>  $ x: num 1
#>  $ y: chr "foo"

str(f(1, 'foo', list(3), fun = sum))
#> List of 4
#>  $ x  : num 1
#>  $ y  : chr "foo"
#>  $    : language list(3)
#>  $ fun: symbol sum

Note match.call only captures the call, and doesn't add in default parameters (there's no y in the first example). Those can be accessed with formals(f), since f isn't primitive, so complete arguments could be created via

user_args <- f(1)
fun_args <- formals(f)
fun_args[names(user_args)] <- user_args

str(fun_args)
#> List of 3
#>  $ x  : num 1
#>  $ y  : num 1
#>  $ ...: symbol

This approach doesn't work well for completed dots, but if they're completed then match.call itself should suffice. To extract the parameters passed to an existing function, you could write a wrapper with match.call, but it's hardly practical to reconstruct every function, and the call you capture will look funny anyway unless you overwrite existing functions. As long as the function isn't primitive, you could use quote to enable the formals approach, though:

cl <- quote(rnorm(5, 2))
user_args <- as.list(cl[-1])    # subset call to only args
fun_args <- formals(as.character(cl[1]))    # subset call to only function
names(user_args) <- names(fun_args)[seq(length(user_args))]
fun_args[names(user_args)] <- user_args

str(fun_args)
#> List of 3
#>  $ n   : num 5
#>  $ mean: num 2
#>  $ sd  : num 1

Another approach is to use rlang, whose functions handle primitives well (fn_fmls(sum)), can extract parts of the call easily and reliably (lang_fn, lang_args), name unnamed parameters accurately (lang_standardize), and more. Together with purrr's new list_modify (dev version), it all becomes fairly painless:

library(rlang)

fun_call <- quo(rnorm(5))
fun_call
#> <quosure: frame>
#> ~rnorm(5)

default_args <- fn_fmls(lang_fn(fun_call))
str(default_args)
#> Dotted pair list of 3
#>  $ n   : symbol 
#>  $ mean: num 0
#>  $ sd  : num 1

user_args <- lang_args(lang_standardise(fun_call))
str(user_args)
#> List of 1
#>  $ n: num 5

calling_args <- purrr::list_modify(default_args, user_args)
str(calling_args)
#> Dotted pair list of 3
#>  $ n   : num 5
#>  $ mean: num 0
#>  $ sd  : num 1

Upvotes: 4

Related Questions