Answer with eval and substitute
I think to do this in this case you just need eval(substitute(expr))
. expr
is a promise, and we can either get the value of the promise by using expr
directly, or the content of the promise, by using substitute
. See http://cran.r-project.org/doc/manuals/R-lang.html#Promise-objects for details. The content of the promise is a call
, so we just eval
that to get the new result.
prettify <- function( dat, expr ) {
labels <- attr(dat,"var.labels")
for(i in seq(ncol(dat))) colnames(dat)[i] <- labels[i]
attr(dat,"var.labels") <- NULL
eval(substitute(expr))
}
> prettify( testDF, str(dat))
'data.frame': 10 obs. of 3 variables:
$ Identifier : int 1 2 3 4 5 6 7 8 9 10
$ Important Data : num 0.336 0.9479 0.1379 0.94 0.0484 ...
$ Lies, Damn Lies, Statistics: num 1.398 0.654 0.268 -0.397 -0.41 ...
In a suggested edit, @user2103369 suggests that replicate
is different because it uses sapply
to get multiple evaluations, so it needs a function rather than a call.
Different behavior when default argument
Interestingly, the promise acts differently depending on if the argument is the default argument or added by the user; see below. I think SoDA addresses this but I don't have it handy. This function prints the value of the promise, evaluates it with eval
, and then evaluates it directly.
foo <- function(a, b=a+1) {
print(substitute(b))
print(eval(substitute(b)))
b
}
Evaluating it directly results in an error when the user supplies the value.
> foo(a=2, b=a+1)
a + 1
[1] 3
Error in foo(a = 2, b = a + 1) : object 'a' not found
But the default value works.
> foo(a=2)
a + 1
[1] 3
[1] 3
In a suggested edit, @user2103369 says that the default argument is evaluated inside the function, while an explicit argument is evaluated in the calling frame. So in this case, the user supplied value fails because a
is not visible in the calling frame.
An alternate method using a function
However, to me (though the OP disagrees; I'm leaving this part for future readers of this answer), this feels like a case where it's more natural to use a function as the second parameter, like this; for one, this means the user doesn't have to know that it's called dat
within the function.
prettify <- function( dat, FUN ) {
f <- match.fun(FUN)
labels <- attr(dat,"var.labels")
for(i in seq(ncol(dat))) colnames(dat)[i] <- labels[i]
attr(dat,"var.labels") <- NULL
f(dat)
}
Then it can be called with an anonymous function, which is exactly what you're looking for, I think, except that the user has to type function(x)
as well.
> prettify( testDF, function(x) str(x) )
'data.frame': 10 obs. of 3 variables:
$ Identifier : int 1 2 3 4 5 6 7 8 9 10
$ Important Data : num 0.296 0.707 0.883 0.821 0.724 ...
$ Lies, Damn Lies, Statistics: num -1.1506 0.4846 -1.824 -0.397 0.0898 ...
Or in simple cases, as in your example, with just the name of the function.
> prettify( testDF, str)
'data.frame': 10 obs. of 3 variables:
$ Identifier : int 1 2 3 4 5 6 7 8 9 10
$ Important Data : num 0.296 0.707 0.883 0.821 0.724 ...
$ Lies, Damn Lies, Statistics: num -1.1506 0.4846 -1.824 -0.397 0.0898 ...