I'd like to capture all arguments and values and return a named list. I'm having trouble with a situation where the value supplied is a function call.
The following produces an object of class "call", which is inconvenient to me, because I want to call paste on it:
fun1 = function(a = 1) as.list(match.call()[-1])
value1 = fun1(a = letters[1:2])
class(value1[[1]])
[1] "call"
paste(value1[[1]], collapse = " - ")
[1] "[ - letters - 1:2" #not useful to me
As a workaround, I can call eval to get the character vector created by c (the lapply function is there to illustrate that when having multiple arguments, eval would be called on all of them):
fun2 = function(a = 1) lapply(as.list(match.call()[-1]), eval)
value2 = fun2(a = letters[1:2])
class(value2[[1]])
[1] "character"
paste(value2[[1]], collapse = " - ")
[1] "a - b" #that's what I want
Is there a better way to do this? Calling eval on a bunch of things just to get the values seems a bit weird to me.
EDIT: The idea behind this is that I would like to pass a list of arguments to a function (which accepts a named list for one of it's arguments) within the original function. I'd like to capture values provided by the user, and default ones (for arguments where the user did not provide anything).
I learned elsewhere that I can get all of that by a combination of match.call and formals. But then, say I need to pass that to the query argument of the httr::GET function, and do some processing on it before I do so (such as adding " - " between "a" and "b"). I then end up with something like "[ - letters - 1:2" instead of "a - b".
I sense that if the answer is using eval, then I am probably asking the wrong question.
I sense that you are looking for something more general, so not sure if this is entirely what you are looking for but its simpler and gives you the desired result. The critical piece here is do.call()
fun1 = function(a = 1) {
L1 <- as.list(match.call())
do.call(paste0, list(L1$a, sep="", collapse=" - "))
}
value1 = fun1(a = letters[1:2])
Well, I think you need to decide which part of your code needs evaluation and what needs not.
It's not entirely clear from your example how general you want to go, but your example-question can be solved by a simple list(), you only need a custom function for providing defaults:
myfun <- function(a=1) list(a=a)
value <- myfun(a=letters[1:2]))
paste(value[[1]], collapse = " - ")
# Basically: value <- list(a=letters[1:2])), or paste(letters[1:2], collapse= " - ")
Generally, you use match.call() without any arguments to find out in what way your function was called. Sometimes it's useful to know whether fun(a=c('a', 'b')) was called, or fun1(a = letters[1:2]), so match.call tells you this, without evaluating anything.
So if you want to actually do something with your arguments, just call them directly, and you can later pass them on to another function
mypaste <- function(..., sep=' -CustomSep- ', collapse=' -Mycollapse- ', prefix='Value:') {
if(length(list(...))>0) {
paste(prefix, ..., sep=sep, collapse=collapse)
} else {
text <- character(0)
}
}
This function is just a variation on paste, but you can make it extensive as you want.
And I get the impression that you want a general case where you match your arguments to the arguments of another function, but to answer that question I'd need to know more about what exactly you are trying to accomplish.
Related
Inside of a Shiny app I am combining levels to make a ID that will be used for ggplot faceting.
Here is an example of what works when doing it by hand:
paste0(data[[1]],', ',data[[2]])
However, the user can select whatever and how ever many identifying columns they want, so I am automating it like this:
First I build combo_ID_build from whatever columns they have selected in UI1 (which is numeric with column numbers for whatever the user selected).
for (i in UI1){
combo_ID_build<-paste0(combo_ID_build,"data[[",i,"]]")
if(i != tail(UI1,1)){
combo_ID_build<-paste0(combo_ID_build,",', ',")
}
}
Then I use eval(parse())
ID <- paste0(eval(parse(text = combo_ID_build)))
So for this example, the user selects columns 1 and 2, so combo_ID_build would be "data[[1]],', ',data[[2]]" where str(combo_ID_build) is chr.
However, when parsed this throws an error, "Error in parse(text = combo_ID_build) : :1:10: unexpected ','
1: data[[1]],"
I tried escaping the comma with \ and \\ but it only throws an error about the backslashes being unrecognized escape strings.
What am I doing wrong here?
You eval(parse()) tries to parse and evaluate an expression. You run
eval(parse(text = "data[[1]],', ',data[[2]]"))
That's equivalent to running the command
data[[1]], ', ', data[[2]]
which indeed is a syntax error. It doesn't matter that you wrapped it in paste0, when you have foo(bar()) foo() will run on the output of bar(). If bar() can't run on its own, there will be an error. You have paste0(eval(parse()), paste0() will run on the output of eval(). So what you eval() needs to run on its own before paste0() is called.
eval(parse()) should usually be avoided. In this case, I think you're looking for do.call, which lets you call a function on a set of arguments in a list. Let's also use paste with sep = ", " to simplify things:
# no loop or eval/parse needed
combo_ID_build = do.call(what = \(x) paste(x, sep = ", "), args = data[UI1])
In this particular case, we can simplify even more by using the utility function interaction() which is made for this use case:
combo_ID_build = interaction(data[UI1], sep = ", ")
I'll leave you with this:
fortunes::fortune(106)
# If the answer is parse() you should usually rethink the question.
# -- Thomas Lumley
# R-help (February 2005)
I'm trying to pass a specific argument dynamically to a function, where the function has default values for most or all arguments.
Here's a toy example:
library(data.table)
mydat <- data.table(evildeeds=rep(c("All","Lots","Some","None"),4),
capitalsins=rep(c("All", "Kinda","Not_really", "Virginal"),
each = 4),
hellprobability=seq(1, 0, length.out = 16))
hellraiser <- function(arg1 = "All", arg2= "All "){
mydat[(evildeeds %in% arg1) & (capitalsins %in% arg2), hellprobability]}
hellraiser()
hellraiser(arg1 = "Some")
whicharg = "arg1"
whichval = "Some"
#Could not get this to work:
hellraiser(eval(paste0(whicharg, '=', whichval)))
I would love a way to specify dynamically which argument I'm calling: In other words, get the same result as hellraiser(arg1="Some") but while picking whether to send arg1 OR arg2 dynamically. The goal is to be able to call the function with only one parameter specified, and specify it dynamically.
You could use some form of do.call like
do.call("hellraiser", setNames(list(whichval), whicharg))
but really this just seems like a bad way to handle arguments for your functions. It might be better to treat your parameters like a list that you can more easily manipulate. Here's a version that allows you to choose values where the argument names are treated like column names
hellraiser2 <- function(..., .dots=list()) {
dots <- c(.dots, list(...))
expr <- lapply(names(dots), function(x) bquote(.(as.name(x)) %in% .(dots[[x]])))
expr <- Reduce(function(a,b) bquote(.(a) & .(b)), expr)
eval(bquote(mydat[.(expr), hellprobability]))
}
hellraiser2(evildeeds="Some", capitalsins=c("Kinda","Not_really"))
hellraiser2(.dots=list(evildeeds="Some", capitalsins=c("Kinda","Not_really")))
This use of ... and .dots= syntax is borrowed from the dplyr standard evaluation functions.
I managed to get the result with
hellraiser(eval(parse(text=paste(whicharg, ' = \"', whichval, '\"', sep=''))))
I want to detect if a variable is missing inside a function without calling the missing() function. I've found two alternatives, but they both seem crude.
Alternative 1
It seems that a variable that is missing has the environmnent class "name" but it seems intuitively wrong to use this construct:
a <- function(a, b){
e <- environment()
if(class(e[["b"]]) == "name")
e$b <- a
print(b)
}
Alternative 2
I guess a possible solution is to use parse and eval but it seems just as crude as the previous solution:
a <- function(a, b){
e <- environment()
if(eval(parse(text = sprintf("missing(%s)", "b"))))
e$b <- a
print(b)
}
Background
I need this as I'm changing the API and I would like to loop over all the old argument names within the ... and send a warning that the user should update to the new parameter names. This is why missing() doesn't work, my current setup is:
# Warnings due to interface changes in 1.0
API_changes <-
c(rowname = "rnames",
headings = "header",
halign = "align.header")
dots <- list(...)
fenv <- environment()
for (i in 1:length(API_changes)){
old_name <- names(API_changes)[i]
new_name <- API_changes[i]
if (old_name %in% names(dots)){
if (class(fenv[[new_name]]) == "name"){
fenv[[new_name]] <- dots[[old_name]]
dots[[old_name]] <- NULL
warning("Deprecated: '", old_name, "'",
" argument is now '", new_name ,"'",
" as of ver. 1.0")
}else{
stop("You have set both the old parameter name: '", old_name, "'",
" and the new parameter name: '", new_name, "'.")
}
}
}
Gosh,-- do we really have to point you to the appropriate fortune entry concerning eval(parse()) ?
Anyway,what's wrong with looping over the contents of dots<-list(...) ? It's not a time-pig by any means.
But my fundamental response is: you've made a mistake by allowing valid or invalid arguments within the ... entries. I don't know why you set up your previous function that way, but it's probably much cleaner, and safer, in the long run to eliminate this construct from your updated release. There's a reason that functions&packages come with help pages. Much as I approve of back-compatibility, I don't think you're doing anyone a favor here. Further, it's not clear to me how or why you'd want a required argument to be passed via ... . And if it's not required, then you don't want to emulate missing in the first place.
Your users will very quickly :-) realize that they've got invalid argument names. Regardless of whether you provide this transitional set of warning messages, they'll either adapt, or emigrate from your code to other options.
I have lots of characters those are actually function definitions. How can I use that characters to execute those function?
The characters I have is as follows:
foo1 <- "function(p1,p2){, v <- 2, print(\"value is \"), print(v)}"
foo2 <- "function(){, cName <- .Call(\"companyNames\"), return(cName)}"
foo3 <- "function(tickers,field,date){,df<-data.frame(Ticker = tickers, Field = field, Date = date), return(df)}"
...etc
I need a general method to execute to all these functions.
EDIT: You've changed your question, so I've amended my answer:
do.call(eval(parse(text=foo1)), list())
You can add a named list to each of those functions in the place of list(). But frankly, what you're attempting is bordering on the absurd. I have no idea how you got into a position where you would need these kinds of tools. You're going to have all kinds of scoping problems from here on in.
Old solution:
fun <- eval(parse(text="function(p1,p2){v <- 2; print(paste0(\"value is \", v))}"))
fun()
## [1] "value is 2"
Does anyone know how to write a function F which takes a function call (say, mean(x = 1:10)) as an argument, and returns just the name of the function being invoked (mean)?
My best attempts so far are summarised below
(function(x1){
return(deparse(substitute(x1)))
})(mean(x = 1:10))
### 'mean(x = 1:10)'
Changing x1 (the function call) to an expression before de-parsing doesn't seem to help much: that returns
(function(x1){
return(deparse(as.expression(substitute(x1))))
})(mean(x = 1:10))
# "expression(mean(x = 1:10))"
If at all possible, I'd like to be able to use anonymous functions as an argument too, so F should return (function(x) print (x)) for (function(x) print (x))(1). If you need any clarification feel free to comment. Thanks.
edit1: just to note, I'd like to avoid checking for the first parenthesis and excising the the code before it (for "mean(x = 1:10)" that would return "mean"), as "bad(Fun_nAme" is actually a legal function name in R.
Question Answered: Josh O'Brien's answer was perfect: the function F that satisfies the above conditions is
F <- function(x) deparse(substitute(x)[[1]])
It works nicely for binary operators, standard functions and anonymous functions.
Here's a simple function that does what you want:
F <- function(x) deparse(substitute(x)[[1]])
F(mean(x=1:10))
# [1] "mean"
F((function(x) print (x))(1))
# [1] "(function(x) print(x))"
F(9+7)
# [1] "+"
I don't know what you're trying to do or if it's a good idea or if this is what you want but here's a whack at it with regex:
FUN <- function(x1){
z <- deparse(substitute(x1))
list(fun=strsplit(z, "\\(")[[c(1, 1)]],
eval=x1)
}
FUN(mean(x = 1:10))