Kalia
Kalia

Reputation: 29

How to create a variable within a function that takes its name from an argument, within R?

I am very much a complete beginner to R, and have struggled to google solutions to my problem, as although I'm sure it's a very basic issue, I find it difficult to explain what I want to do. I appreciate that stackoverflow is only for serious programmers, so if anyone can even just point to someone else's question that deals with the same subject, I would be incredibly appreciate. With that being said:

What I want to do is have a function be able to read and assign to the name of a vector, not just its contents. When I wrote:

num <- 5
func <- function(x) {
    x <<- 8
}
func(num)

I thought it would create a new vector called "num". -- as in, it would read it like:

func(num) 
num <<- 8. 

But instead it either just creates a new vector literally called "x", and assigns "8" to it, or it tries to assign "8" to "5", I can't really tell which. Either way, how can I tell R that I want it to utilise what I wrote in the argument as the vector name?

Upvotes: 1

Views: 510

Answers (2)

bgoldst
bgoldst

Reputation: 35324

Before I provide solutions, I think it would be best to address a misconception about argument passing. When you call a function as

func(num)

you are not passing the name of the variable as the argument value, you are passing the value of the variable as the argument value. In other words, if num is equal to 5, then you are passing the number 5. If the function uses standard argument evaluation, then it will have no knowledge of the variable name that was used in the argument, or even if there was a variable name at all (there may not be, if the argument value was produced from a literal or an expression).

If you want to pass the variable name as the argument value, you need to quote it:

func('num')

That being said, there is something called non-standard argument evaluation. R is fairly unusual in that it provides a mechanism for retrieving the argument expression that was bound to a parameter, and this actually could allow you to retrieve the variable name from inside the function. The substitute() function is often used for this purpose. The variable name used as the argument expression does not even have to correspond to an existing variable for this to work.

f <- function(x) as.character(substitute(x));
f(some.var);
## [1] "some.var"

Many base R functions make use of non-standard argument evaluation, such as rm() and library().


There are several ways to do what you want.

1: assign()

The assign() function can be used to assign a parameterized variable name in any environment. The target of your assignment appears to be the global environment, sometimes called the workspace environment, which can be accessed with globalenv() (or .GlobalEnv).

f <- function(x) assign(x,8,envir=globalenv());
num;
## Error: object 'num' not found
f('num');
num;
## [1] 8

2: environment access

It is possible to dereference an environment object and assign it directly, although strangely, it appears that function calls or expressions that return environment objects cannot be used as lvalues in this way. In other words, you need to capture the environment object in a variable before you can dereference and assign it.

f <- function(x) { e <- globalenv(); e[[x]] <- 9; };
num;
## Error: object 'num' not found
f('num');
num;
## [1] 9
globalenv()$num <- 'why doesn\'t this work???';
## Error in globalenv()$num <- "why doesn't this work???" :
##   invalid (NULL) left side of assignment

3: parse(), eval(), and <<-

We can use the superassignment operator to assign to a dynamic name by evaluating it dynamically.

f <- function(x) eval(parse(text=paste0(x,' <<- 10;')));
num;
## Error: object 'num' not found
f('num');
num;
## [1] 10

Any of the above approaches can be combined with non-standard argument evaluation, for example:

f <- function(x) assign(as.character(substitute(x)),11,envir=globalenv());
num;
## Error: object 'num' not found
f(num);
num;
## [1] 11

Upvotes: 1

Paul Hiemstra
Paul Hiemstra

Reputation: 60984

You can use the return statement of the function for this:

num = 5
func = function(x) {
    return(x)
}
num = func(8)
num
# [1] 8

See also this tutorial I wrote, this provides some more in depth informatie on functions, scoping etc.

Also note that in general there is no need for the <<- operator, as this can lead to hard to understand code. See for example this SO post.

Upvotes: 3

Related Questions