I need to forward ... to a function argument, extract the symbols as code, and convert them to characters, all while preserving names. I usually use match.call(expand.dots = FALSE)$... for this, but it mangles the !! operator.
Current behavior
f <- function(...){
match.call(expand.dots = FALSE)$...
}
f(a = 1234, b = !!my_variable)
## $a
## [1] 1234
##
## $b
## !(!my_variable)
Desired behavior
f <- function(...){
# whatever works
}
f(a = 1234, b = !!my_variable)
## $a
## [1] 1234
##
## $b
## !!my_variable
EDIT Even better:
f <- function(...){
# whatever works
}
my_variable <- "my value"
f(a = 1234, b = !!my_variable)
## $a
## [1] 1234
##
## $b
## [1] "my_value"
The following code appears to work. The use case is here. Thanks to MrFlick for nudging me in the right direction.
f <- function(...){
rlang::exprs(...)
}
my_variable <- "my value"
f(a = 1234, b = !!my_variable)
## $a
## [1] 1234
##
## $b
## [1] "my_value"
EDIT: by the way, I am still looking for a way to parse !! without evaluating it. This would enhance user-side functionality related to https://github.com/ropensci/drake/issues/200.
Related
I would like to capture a function's arguments within its body to help with logging. I have found that match.call() and sys.call() work when the argument value is explicitly stated in the function call, but don't output an evaluated value when an object name is used.
Here's a simplified example:
gauss_vector <- function(number) {
sys_args <- as.list(sys.call())
match_args <- as.list(match.call())
output <- rnorm(n = number)
list(sys_args,
match_args,
output)
}
When this function is called like this:
gauss_vector(number = 5)
The resulting list includes the value 5.
[[1]]
[[1]][[1]]
gauss_vector
[[1]]$number
[1] 5
[[2]]
[[2]][[1]]
gauss_vector
[[2]]$number
[1] 5
[[3]]
[1] 0.9663434 0.8051087 0.1576298 0.3189806 -2.3110680
However, when the function is called like this:
n <- 5
gauss_vector(number = n)
The resulting list only includes n.
[[1]]
[[1]][[1]]
gauss_vector
[[1]]$number
n
[[2]]
[[2]][[1]]
gauss_vector
[[2]]$number
n
[[3]]
[1] -0.6017670 -0.7631405 0.7793892 -0.7529637 1.3022802
Is there a way to capture the evaluated figure rather than the object name when the function is called in the second way?
You could eval all the arguments passed to the function.
gauss_vector <- function(number) {
sys_args <- as.list(sys.call())
sys_args[-1] <- lapply(sys_args[-1], eval)
match_args <- as.list(match.call())
match_args[-1] <- lapply(match_args[-1], eval)
output <- rnorm(n = number)
list(sys_args,match_args,output)
}
gauss_vector(n)
#[[1]]
#[[1]][[1]]
#gauss_vector
#[[1]][[2]]
#[1] 5
#[[2]]
#[[2]][[1]]
#gauss_vector
#[[2]]$number
#[1] 5
#[[3]]
#[1] 0.6998265 0.4037748 1.8558809 -0.1343624 -1.5600925
I'd like to create a function to accept indefinite number of arguments.
Is there a way to define a function in R?
Thanks.
you can use the dots like this for example
f <- function(...) {
arguments <- list(...)
print(arguments)
}
f(a=1, b=2)
## $a
## [1] 1
## $b
## [1] 2
I am new to R and have a question on the function posted here: R RStudio Resetting debug / function environment. Why are the objects set to themselves (e.g. "getmean = getmean" etc.)? Couldn't it simply be written as follows: list(set, get, setmean, getmean)
The difference is that
aa <- list(set, get, setmean, getmean)
is an unnamed list and
bb <- list(set=set, get=get, setmean=setmean, getmean=getmean)
is a named list. Compare names(aa) and names(bb).
And that = is not assignment. It's really just giving a label to a list item. It's one of the reasons R programmers try to only use <- for assignment and leave = with this special meaning. You could have easily also done
cc <- list(apple=set, banana=get, ornage=setmean, grape=getmean)
cc$apple()
It doesn't have to be the exact same name.
Because list(set, get, setmean, getmean) won't tag the list elements with the correct names. Here's an example of the difference between tagged and untagged lists:
> list(1, 2, 3)
[[1]]
[1] 1
[[2]]
[1] 2
[[3]]
[1] 3
> list(foo=1, bar=2, baz=3)
$foo
[1] 1
$bar
[1] 2
$baz
[1] 3
Note that in the context of argument lists, = is used to supply named arguments, it does not do any assignments (unlike <-). Thus list(foo=1, bar=2, baz=3) is very different from list(foo<-1, bar<-2, baz<-3).
The question has been answered, but you could also do this to achieve the same result.
> object <- c('set', 'get', 'setmean', 'getmean')
> setNames(object = as.list(object), nm = object)
# $set
# [1] "set"
#
# $get
# [1] "get"
#
# $setmean
# [1] "setmean"
#
# $getmean
# [1] "getmean"
The quotations are dependent on what these values actually are.
And you can set different names with like this
> setNames(as.list(object), letters[1:4])
# $a
# [1] "set"
#
# $b
# [1] "get"
#
# $c
# [1] "setmean"
#
# $d
# [1] "getmean"
setNames comes in handy when working with lapply.
The title is the self-contained question. An example clarifies it: Consider
x=list(a=1, b="name")
f <- function(){
assign('y[["d"]]', FALSE, parent.frame() )
}
g <- function(y) {f(); print(y)}
g(x)
$a
[1] 1
$b
[1] "name"
whereas I would like to get
g(x)
$a
[1] 1
$b
[1] "name"
$d
[1] FALSE
A few remarks. I knew what is wrong in my original example, but am using it to make clear my objective. I want to avoid <<-, and want x to be changed in the parent frame.
I think my understanding of environments is primitive, and any references are appreciated.
The first argument to assign must be a variable name, not the character representation of an expression. Try replacing f with:
f <- function() with(parent.frame(), y$d <- FALSE)
Note that a, b and d are list components, not list attributes. If we wanted to add an attribute "d" to y in f's parent frame we would do this:
f <- function() with(parent.frame(), attr(y, "d") <- FALSE)
Also, note that depending on what you want to do it may (or may not) be better to have x be an environment or a proto object (from the proto package).
assign's first argument needs to be an object name. Your use of assign is basically the same as the counter-example at the end of the the assign help page. Observe:
> x=list(a=1, b="name")
> f <- function(){
+ assign('x["d"]', FALSE, parent.frame() )
+ }
> g <- function(y) {f(); print(`x["d"]`)}
> g(x)
[1] FALSE # a variable with the name `x["d"]` was created
This may be where you want to use "<<-" but it's generally considered suspect.
> f <- function(){
+ x$d <<- FALSE
+ }
> g <- function(y) {f(); print(y)}
> g(x)
$a
[1] 1
$b
[1] "name"
$d
[1] FALSE
A further thought, offered in the absence of any goal for this exercise and ignoring the term "attributes" which Gabor has pointed out has a specific meaning in R, but may not have been your goal. If all you want is the output to match your specs then this achieves that goal but take notice that no alteration of x in the global environment is occurring.
> f <- function(){
+ assign('y', c(x, d=FALSE), parent.frame() )
+ }
> g <- function(y) {f(); print(y)}
> g(x)
$a
[1] 1
$b
[1] "name"
$d
[1] FALSE
> x # `x` is unchanged
$a
[1] 1
$b
[1] "name"
The parent.frame for f is what might be called the "interior of g but the alteration does not propagate out to the global environment.
How to create an "empty object" in R? [edit: I don't know how to rightly call this "thing" so I am calling it "empty object", others: "empty symbol", "zero length symbol", "missing object" might also be used]
[edit2: finally I tend to settle down on the "missing symbol object" for the name of the "thing". It also appears that J.Chambers is using this terminology in his 2008 book, see comments for #mbq's answer. According to Chambers, the "missing symbol" has a zero-length string as it's contents. Thus, as.symbol("") should create such an object, which it doesn't in current version of R(2.11.1) ]
The simplest way I could find is
x <- alist(a=)$a
[Clarification]
Note that "empty object" is not a NULL object or a vector of length 0.
"Empty object" x in my above example could be used in the function's formals manipulation, which is what I need it for.
Here is an example:
> al <- alist(a = 323, b = , c = 434)
> al
$a
[1] 323
$b
$c
[1] 434
>
> al[["c"]] <- numeric()
> al
$a
[1] 323
$b
$c #not empty
numeric(0)
>
> al[["c"]] <- list()
> al
$a
[1] 323
$b
$c #not empty
list()
>
>
> al[["c"]] <- NULL #object removed
> al
$a
[1] 323
$b
>
> al[["c"]] <- alist(a = )$a
> al
$a
[1] 323
$b
$c #empty
So, I am just looking for a way to create empty objects for use in function's formals manipulations. I am pretty sure there must be a way in base R.
Here is an example:
> foo <- function(a = 3232, b = 234){b+a}
> formals(foo)
$a
[1] 3232
$b
[1] 234
> formals(foo)$c <- alist(a = )$a
> formals(foo)
$a
[1] 3232
$b
[1] 234
$c
> foo <- function(a = 3232, b = 234){b+a}
> formals(foo)
$a
[1] 3232
$b
[1] 234
> formals(foo)$c <- alist(a = )$a
> formals(foo)
$a
[1] 3232
$b
[1] 234
$c
Thanks.
Try substitute():
foo <- function(a = 3232, b = 234) {}
formals(foo)$c <- substitute()
foo
# function (a = 3232, b = 234, c)
What have you done with alist(a=)$a is not an empty object, it is empty symbol/name ('' compiled as symbol name). I'm pretty sure it can't be replicated in any other way (as.symbol('') raises error).
As others have said, you're not creating an empty object. x holds a value, which is an unevaluated expression.
?alist says:
‘alist’ handles its arguments as if they described function arguments.
So the values are not evaluated, and tagged arguments with no value are
allowed whereas ‘list’ simply ignores them. ‘alist’ is most often used in
conjunction with ‘formals’.
This is the easiest way I can determine to add an argument with no value to a function:
> foo <- function(a = 3232, b = 234){b+a}
> formals(foo) <- c(formals(foo),alist(c=))
> formals(foo)
$a
[1] 3232
$b
[1] 234
$c
Depending on the type :
x <- list()
# empty vectors
y <- numeric(0)
z <- logical(0)
...
Now you have to think whether you really want to create an empty object.
edit : You can indeed use the NULL option as well. The main difference is that the NULL object doesn't have a type. See also ?"NULL"
There is no 'empty object'. There's NULL, NA (in various flavours), zero-length lists and so on. Where does your concept of 'empty object' come from, and what are the properties of an 'empty object'?
What you have created with alist is a monster:
> x<-alist(a=)$a
> x
Error: argument "x" is missing, with no default
[its some kind of unevaluated function argument expression thing. Not an 'empty object' in any sense of the word I know]
Also d <- c()
Also d <- ""
(padding padding padding)