Finding the names of all functions in an R expression - r

I'm trying to find the names of all the functions used in an arbitrary legal R expression, but I can't find a function that will flag the below example as a function instead of a name.
test <- expression(
this_is_a_function <- function(var1, var2){
this_is_a_function(var1-1, var2)
})
all.vars(test, functions = FALSE)
[1] "this_is_a_function" "var1" "var2"
all.vars(expr, functions = FALSE) seems to return functions declarations (f <- function(){}) in the expression, while filtering out function calls ('+'(1,2), ...).
Is there any function - in the core libraries or elsewhere - that will flag 'this_is_a_function' as a function, not a name? It needs to work on arbitrary expressions, that are syntactically legal but might not evaluate correctly (e.g '+'(1, 'duck'))
I've found similar questions, but they don't seem to contain the solution.
If clarification is needed, leave a comment below. I'm using the parser package to parse the expressions.
Edit: #Hadley
I have expressions with contain entire scripts, which usually consist of a main function containing nested function definitions, with a call to the main function at the end of the script.
Functions are all defined inside the expressions, and I don't mind if I have to include '<-' and '{', since I can easy filter them out myself.
The motivation is to take all my R scripts and gather basic statistics about how my use of functions has changed over time.
Edit: Current Solution
A Regex-based approach grabs the function definitions, combined with the method in James' comment to grab function calls. Usually works, since I never use right-hand assignment.
function_usage <- function(code_string){
# takes a script, extracts function definitions
require(stringr)
code_string <- str_replace(code_string, 'expression\\(', '')
equal_assign <- '.+[ \n]+<-[ \n]+function'
arrow_assign <- '.+[ \n]+=[ \n]+function'
function_names <- sapply(
strsplit(
str_match(code_string, equal_assign), split = '[ \n]+<-'),
function(x) x[1])
function_names <- c(function_names, sapply(
strsplit(
str_match(code_string, arrow_assign), split = '[ \n]+='),
function(x) x[1]))
return(table(function_names))
}

Short answer: is.function checks whether a variable actually holds a function. This does not work on (unevaluated) calls because they are calls. You also need to take care of masking:
mean <- mean (x)
Longer answer:
IMHO there is a big difference between the two occurences of this_is_a_function.
In the first case you'll assign a function to the variable with name this_is_a_function once you evaluate the expression. The difference is the same difference as between 2+2 and 4.
However, just finding <- function () does not guarantee that the result is a function:
f <- function (x) {x + 1} (2)
The second occurrence is syntactically a function call. You can determine from the expression that a variable called this_is_a_function which holds a function needs to exist in order for the call to evaluate properly. BUT: you don't know whether it exists from that statement alone. however, you can check whether such a variable exists, and whether it is a function.
The fact that functions are stored in variables like other types of data, too, means that in the first case you can know that the result of function () will be function and from that conclude that immediately after this expression is evaluated, the variable with name this_is_a_function will hold a function.
However, R is full of names and functions: "->" is the name of the assignment function (a variable holding the assignment function) ...
After evaluating the expression, you can verify this by is.function (this_is_a_function).
However, this is by no means the only expression that returns a function: Think of
f <- function () {g <- function (){}}
> body (f)[[2]][[3]]
function() {
}
> class (body (f)[[2]][[3]])
[1] "call"
> class (eval (body (f)[[2]][[3]]))
[1] "function"
all.vars(expr, functions = FALSE) seems to return functions declarations (f <- function(){}) in the expression, while filtering out function calls ('+'(1,2), ...).
I'd say it is the other way round: in that expression f is the variable (name) which will be asssigned the function (once the call is evaluated). + (1, 2) evaluates to a numeric. Unless you keep it from doing so.
e <- expression (1 + 2)
> e <- expression (1 + 2)
> e [[1]]
1 + 2
> e [[1]][[1]]
`+`
> class (e [[1]][[1]])
[1] "name"
> eval (e [[1]][[1]])
function (e1, e2) .Primitive("+")
> class (eval (e [[1]][[1]]))
[1] "function"

Instead of looking for function definitions, which is going to be effectively impossible to do correctly without actually evaluating the functions, it will be easier to look for function calls.
The following function recursively spiders the expression/call tree returning the names of all objects that are called like a function:
find_calls <- function(x) {
# Base case
if (!is.recursive(x)) return()
recurse <- function(x) {
sort(unique(as.character(unlist(lapply(x, find_calls)))))
}
if (is.call(x)) {
f_name <- as.character(x[[1]])
c(f_name, recurse(x[-1]))
} else {
recurse(x)
}
}
It works as expected for a simple test case:
x <- expression({
f(3, g())
h <- function(x, y) {
i()
j()
k(l())
}
})
find_calls(x)
# [1] "{" "<-" "f" "function" "g" "i" "j"
# [8] "k" "l"

Just to follow up here as I have also been dealing with this problem: I have now created a C-level function to do this using code very similar to the C implementation of all.names and all.vars in base R. It however only works with objects of type "language" i.e. function calls, not type "expression". Demonstration:
ex = quote(sum(x) + mean(y) / z)
all.names(ex)
#> [1] "+" "sum" "x" "/" "mean" "y" "z"
all.vars(ex)
#> [1] "x" "y" "z"
collapse::all_funs(ex)
#> [1] "+" "sum" "/" "mean"
Created on 2022-08-17 by the reprex package (v2.0.1)
This generalizes to arbitrarily complex nested calls.

Related

How can one make visible the difference in the outputs of quote() and substitute()?

As applied to the same R code or objects, quote and substitute typically return different objects. How can one make this difference apparent?
is.identical <- function(X){
out <- identical(quote(X), substitute(X))
out
}
> tmc <- function(X){
out <- list(typ = typeof(X), mod = mode(X), cls = class(X))
out
}
> df1 <- data.frame(a = 1, b = 2)
Here the printed output of quote and substitute are the same.
> quote(df1)
df1
> substitute(df1)
df1
And the structure of the two are the same.
> str(quote(df1))
symbol df1
> str(substitute(df1))
symbol df1
And the type, mode and class are all the same.
> tmc(quote(df1))
$typ
[1] "symbol"
$mod
[1] "name"
$cls
[1] "name"
> tmc(substitute(df1))
$typ
[1] "symbol"
$mod
[1] "name"
$cls
[1] "name"
And yet, the outputs are not the same.
> is.identical(df1)
[1] FALSE
Note that this question shows some inputs that cause the two functions to display different outputs. However, the outputs are different even when they appear the same, and are the same by most of the usual tests, as shown by the output of is.identical() above. What is this invisible difference, and how can I make it appear?
note on the tags: I am guessing that the Common LISP quote and the R quote are similar
The reason is that the behavior of substitute() is different based on where you call it, or more precisely, what you are calling it on.
Understanding what will happen requires a very careful parsing of the (subtle) documentation for substitute(), specifically:
Substitution takes place by examining each component of the parse tree
as follows: If it is not a bound symbol in env, it is unchanged. If it
is a promise object, i.e., a formal argument to a function or
explicitly created using delayedAssign(), the expression slot of the
promise replaces the symbol. If it is an ordinary variable, its value
is substituted, unless env is .GlobalEnv in which case the symbol is
left unchanged.
So there are essentially three options.
In this case:
> df1 <- data.frame(a = 1, b = 2)
> identical(quote(df1),substitute(df1))
[1] TRUE
df1 is an "ordinary variable", but it is called in .GlobalEnv, since env argument defaults to the current evaluation environment. Hence we're in the very last case where the symbol, df1, is left unchanged and so it identical to the result of quote(df1).
In the context of the function:
is.identical <- function(X){
out <- identical(quote(X), substitute(X))
out
}
The important distinction is that now we're calling these functions on X, not df1. For most R users, this is a silly, trivial distinction, but when playing with subtle tools like substitute it becomes important. X is a formal argument of a function, so that implies we're in a different case of the documented behavior.
Specifically, it says that now "the expression slot of the promise replaces the symbol". We can see what this means if we debug() the function and examine the objects in the context of the function environment:
> debugonce(is.identical)
> is.identical(X = df1)
debugging in: is.identical(X = df1)
debug at #1: {
out <- identical(quote(X), substitute(X))
out
}
Browse[2]>
debug at #2: out <- identical(quote(X), substitute(X))
Browse[2]> str(quote(X))
symbol X
Browse[2]> str(substitute(X))
symbol df1
Browse[2]> Q
Now we can see that what happened is precisely what the documentation said would happen (Ha! So obvious! ;) )
X is a formal argument, or a promise, which according to R is not the same thing as df1. For most people writing functions, they are effectively the same, but the internal implementation disagrees. X is a promise object, and substitute replaces the symbol X with the one that it "points to", namely df1. This is what the docs mean by the "expression slot of the promise"; that's what R sees when in the X = df1 part of the function call.
To round things out, try to guess what will happen in this case:
is.identical <- function(X){
out <- identical(quote(A), substitute(A))
out
}
is.identical(X = df1)
(Hint: now A is not a "bound symbol in the environment".)
A final example illustrating more directly the final case in the docs with the confusing exception:
#Ordinary variable, but in .GlobalEnv
> a <- 2
> substitute(a)
a
#Ordinary variable, but NOT in .GlobalEnv
> e <- new.env()
> e$a <- 2
> substitute(a,env = e)
[1] 2

How to get the list of in-built functions used within a function

Lets say I have a function named Fun1 within which I am using many different in-built functions of R for different different processes. Then how can I get a list of in-built functions used inside this function Fun1
Fun1 <- function(x,y){
sum(x,y)
mean(x,y)
c(x,y)
print(x)
print(y)
}
So My output should be like list of characters i.e. sum, mean, c, print. Because these are the in-built functions I have used inside function Fun1.
I have tried using grep function
grep("\\(",body(Fun1),value=TRUE)
# [1] "sum(x, y)" "mean(x, y)" "c(x, y)" "print(x)" "print(y)"
It looks ok, but arguments should not come i.e. x and y. Just the list of function names used inside body of function Fun1 here.
So my overall goal is to print the unique list of in-built functions or any create functions inside a particular function, here Fun1.
Any help on this is highly appreciated. Thanks.
You could use all.vars() to get all the variable names (including functions) that appear inside the body of Fun1, then compare that with some prepared list of functions. You mention in-built functions, so I will compare it with the base package object names.
## full list of variable names inside the function body
(vars <- all.vars(body(Fun1)[-1], functions = TRUE))
# [1] "sum" "x" "y" "mean" "c" "print"
## compare it with the base package object names
intersect(vars, ls(baseenv()))
# [1] "sum" "mean" "c" "print"
I removed the first element of the function body because presumably you don't care about {, which would have been matched against the base package list.
Another possibility, albeit a bit less reliable, would be to compare the formal arguments of Fun1 to all the variable names in the function. Like I said, likely less reliable though because if you make assignments inside the function you will end up with incorrect results.
setdiff(vars, names(formals(Fun1)))
# [1] "sum" "mean" "c" "print"
These are fun though, and you can fiddle around with them.
Access to the parser tokens is available with functions from utils.
tokens <- utils::getParseData(parse(text=deparse(body(Fun1))))
unique(tokens[tokens[["token"]] == "SYMBOL_FUNCTION_CALL", "text"])
[1] "sum" "mean" "c" "print"
This should be somewhat helpful - this will return all functions however.
func_list = Fun1 %>%
body() %>% # extracts function
toString() %>% # converts to single string
gsub("[{}]", "", .) %>% # removes curly braces
gsub("\\s*\\([^\\)]+\\)", "", .) %>% # removes all contents between brackets
strsplit(",") %>% # splits strings at commas
unlist() %>% # converts to vector
trimws(., "both") # removes all white spaces before and after`
[1] "" "sum" "mean" "c" "print" "print"
> table(func_list)
func_list
c mean print sum
1 1 1 2 1
This is extremely limited to your example... you could modify this to be more robust. It will fall over where a function has brackets nesting other functions etc.
this is not so beautiful but working:
Fun1 <- function(x,y){
sum(x,y)
mean(x,y)
c(x,y)
print(x)
print(y)
}
getFNamesInFunction <- function(f.name){
f <- deparse(body(get(f.name)))
f <- f[grepl(pattern = "\\(", x = f)]
f <- sapply(X = strsplit(split = "\\(", x = f), FUN = function(x) x[1])
unique(trimws(f[f != ""]))
}
getFNamesInFunction("Fun1")
[1] "sum" "mean" "c" "print"
as.list(Fun1)[3]
gives you the part of the function between the curly braces.
{
sum(x, y)
mean(x, y)
c(x, y)
print(x)
print(y)
}
Hence
gsub( ").*$", "", as.list(Fun1)[3])
gives you everything before the first " ) " appears which is presumable the name of the first function.
Taking this as a starting point you should be able to include a loop which gives you the other functions and not the first only the first one.

Recognizing language type arguments in function calls

I want to create an overloaded function that behaves differently given the arguments provided. For this, I need to check if the argument given is an existing object (e.g. data frame, list, integer) or an abstract formula (e.g. a + b, 2 * 4, y ~ x + y etc.). Below I paste what I would like it to recognize:
df <- data.frame(a, b)
f(df) # data.frame
f(data.frame(a, b)) # data frame
f(a + b) # expression
f("a + b") # character
f(2 * 2 + 7) # expression
f(I(2 * 2)) # integer
Is it possible to construct such a function? How? Unfortunately I wasn't able to find any references on the web or in the books on R programming I know.
The general way of overloading functions in R would be something like this:
f <- function(x) UseMethod("f")
f.default <- function(x) eval(substitute(x))
f.data.frame <- function(x) print("data frame")
It gives:
> f(df)
[1] "data frame"
> f(2 + 2)
[1] 4
> f(list(a, b))
[[1]]
[1] 1
[[2]]
[1] 2
So the problem with doing it like this is that I would have to name all the possible other data types rather than checking if x is an expression.
The same is with using:
f2 <- function(x) typeof(substitute(x))
because it evaluates function calls and expressions in the same manner:
> f2(2 + 2)
[1] "language"
> f2(df)
[1] "symbol"
> f2(data.frame(a, b))
[1] "language"
while I would like it to differentiate between list(a, b) and 2 + 2, because the first one is a list, and the second one is an expression.
I know that it would be easy with a classic R formula that is easily recognizable by R, but is it possible with different input?
Thanks!
It is the principle of object oriented langage in R. You should learn a bit more about it here:
https://www.stat.auckland.ac.nz/~stat782/downloads/08-Objects.pdf
http://brainimaging.waisman.wisc.edu/~perlman/R/A1%20Introduction%20to%20object-oriented%20programming.pdf
There are two types of objects in R: S3 and S4. S3 objects are easier to implement and more flexible. Their use is sufficient for what you want to do. You can use S3 generic functions.
I strongly advise you to learn more about these S3 and S4 classes, but to make it short, you can just look at the class of parameter you give to function f. This can be done thanks to function class.
You can separate your function f in different cases:
f <- function(a){
if (class(a) == 'data.frame'){
# do things...
}
else if (class(a) == 'formula'){
# do things...
}
else if (class(a) == 'integer'){
# do things...
}
else {
stop("Class no supported")
}
}
OK, it seems I tried to complicate it in a greater extent than I had to. The simple answer is just:
if (tryCatch(is.data.frame(x), error=function(z) FALSE)) {
# here do stuff with a data.frame
} else {
# here check the expression using some regular expressions etc.
}

R: disjoint complex number when setting function body

While constructing a function I discovered, that complex numbers are disjoint when setting the function body:
funCplx <- function () {}
formals(funCplx) <- alist(t=)
body(funCplx) <- as.call(c(as.name("{"), parse(text = "t*(2+3i)")))
funCplx
funCplx(3)
As you can see, the function returns the correct value, but contains t * (2+(0+3i)). It is disjoint within the c(). The expression c(parse(text = "t*(2+3i)")) returns the disjoint term, whereas parse(text = "t*(2+3i)") returns t*(2+3i).
Why is the expression disjoint in the body?
Edit: I add an argument to the function. I removed it for sparseness, but it gets necessary to get the correct solution.
Edit 2: Most answers assume I know the complex number (here 2+3i). I took this example, because it is minimal.
In my real case, I take many complex numbers from variable matrices and put them together in one function with additional variables (here t) and functions like exp. In my first step I paste different parts and parse this text into the function body.
To point out my question: Is it bad that the complex number is disjoint in the parse function in case of calculation steps? Reworded: Does R need more calculation steps to calculate 5*(2+(0+3i)) than 5*(2+3i)?
Why not:
funCplx <- function () {}
formals(funCplx) <- alist()
body( funCplx) <- as.call(c(as.name("{"), eval(parse(text = "2+3i"))) )
funCplx
funCplx()
Returns:
funCplx
#----console displays---
function ()
{
2+3i
}
With the added request to include extra arguments in what appears when the function is print-ed, you could make it look more "finished" by assigning the fixed value to a name and then using that name in the expression:
funCplx <- function () {}
formals(funCplx) <- alist(t=)
environment(funCplx) <- new.env()
environment(funCplx)$cplx <- (2+3i)
body(funCplx) <- quote(t*cplx)
funCplx
# function (t)
# t * cplx
funCplx(3)
# [1] 6+9i
And testing to see if the earlier flimsy construction error was fixed:
> cplx=42
> funCplx(7)
[1] 14+21i
The earlier method didn't create an environment for funCplx and so its environment was .GlobalEnv. This version will carry 'cplx' around with it.
To create an expression that multiplies an argument by a given complex constant, use this:
funCplx <- function () {}
formals(funCplx) <- alist(t=)
body(funCplx) <- as.call(c(as.name("{"), call("*", as.symbol("t"), 2+3i)))
funCplx
funCplx(3)
Note that this avoids evaluation of 2 + (0+3i) at "run-time", since the constant is "hard-coded" in the function's body:
> body(funCplx)[[2]][[3]]
[1] 2+3i
> is(body(funCplx)[[2]][[3]])
[1] "complex" "vector"

getting the arguments of a parent function in R, with names

I'm trying to write a function that captures the arguments of the function it is called from. For example,
get_args <- function () as.list( sys.call(sys.parent()) )[-1]
caller <- function (x, y, z) {
get_args()
}
caller(1,2,3)
[[1]]
[1] 1
[[2]]
[1] 2
[[3]]
[1] 3
sys.call() unfortunately does not add match parameter names with argument values, and I'd like to write a similar version of get_args that returns output similar to
caller2 <- function (x, y, z) {
as.list( match.call() )[-1]
}
caller2(1,2,3)
$x
[1] 1
$y
[1] 2
$z
[1] 3
replacing "get_args()" with "match.call()" directly is not the solution I'm looking for, since in reality get_args will do some other things before returning its parent functions arguments.
I've tried to use match.call() with sys.parent() in several ways, but I can't get the function to return the arguments of caller; it just returns the arguments of get_args().
Is there any way to make get_args() return output identical to that of caller2 for the above test case? I know that naming the arguments manually is possible using formals(), but is this guaranteed to be equivelant?
If any clarification is needed, leave a comment below. Thanks.
EDIT 1:
the aim of get_args() is to act as a user-friendly way of getting the arguments with which a function was called. Typing as.list( match.call() )[-1] gets old, but because match.call grabs the nearest function call it just gets the arguments of get_args() at the moment.
get_args() will also get default arguments from the parent function, but this easy to implement.
SOLUTION:
thanks Hong Ooi, the key to using match.call seems to be providing both the call and the definition of the function you want to find out about. A slightly modified, anonymous-friendly version of get_args is below for posterity
get_args <- function () {
as.list( match.call(
def = sys.function( -1 ),
call = sys.call(-1)) )[-1]
}
This version finds the function further up the call stack, grabs its definition and call, and matches parameters to its arguments.
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.

Resources