Reputation: 11128
I am not sure if this has been asked here, But I am very confused here. I am reading this awesome book called Advanced R by Hadley Wickham from here.
There is function called cement
that has been described here, I have modified it little bit and trying to understand it.
library(rlang)
cement1 <- function(x) {
dots <- expr(x)
print(class(dots))
#paste(expr_name(x))
}
cement2 <- function(y,z) {
dots <- exprs(y,z)
print(class(dots))
#paste(purrr::map(dots, expr_name), collapse = " ")
}
Running the above cement1
without any parameter returns me the class of dots as "name".
However, when I run the cement2
function with additional parameter, the class returns "list", {simply putting class(expr(x)) returns "name" whereas class(exprs(x)) returns "list"}.
I am not getting my head around this as why it is printing different class returned by expr
and exprs
. The only difference I thought I knew about them was, one deals with one parameter, other one deals with multiple parameters, but I may be wrong, I might have missed some details.
Original Problem: So, it all started by running these two functions separately by removing the comments section in the code for both cement1
and cement2
, when I run the functions Below are the output returned by them:
cement1(Hello) #Returns , Error in type_of(.x) : object 'Hello' not found
cement2(Hello) #Works very well and returns, [1] "y z"
So I tried to find the reason why cement1
failed and then printed their classes and that is when I realized , expr
and exprs
return different classes.
My question is:
1) Are they by design, if yes then why? Or, I am doing some horrible mistake, which I am currently unable to see.
2) Does cement1
can't work this if not , what is the correct way?
I am sorry for too long sentences, My first language is not English, hence If anything silly is there, Please let me know I shall correct it. I hope this is not a duplicate, I tried to find the answer but could not found by my own.
Thanks for any help.
R Version: 3.4.2 rlang: 0.2.0
Upvotes: 4
Views: 581
Reputation: 13691
1) Yes, the return values of expr
and exprs
differ by design. From the ?expr
help page:
enexpr() and expr() capture a single raw expression.
enexprs() and exprs() capture a list of raw expressions including expressions contained in
...
.
2) expr_name()
expects a quoted expression, such as what's produced by expr()
. So, you need to modify your cement1
to call expr_name()
on dots
, not x
. You can also remove paste
because you are not concatenating anything.
cement1 <- function(x) {
dots <- expr(x)
# print(class(dots)) ## Commented out for clarity
expr_name(dots) ## The input to expr_name is now effectively expr(x)
}
cement1( Hello )
# "x"
Your function cement2
basically calls expr_name()
on every element of the list returned by exprs()
, then concatenates the results into a single string.
2a) Now that we got your cement1
working, we can improve it further. Currently, the function doesn't make any use of its input argument x
. expr()
simply captures the unevaluated expression, and this expression will always be x
, regardless of what you name your argument:
cement1.1 <- function( completelyIgnoredName ) {
dots <- expr(x)
expr_name(dots)
}
cement1.1( Hello )
# "x"
However, if you replace expr()
with enexpr()
, the function will substitute the expression provided as the function's argument and capture that instead:
cement1.2 <- function(x) {
dots <- enexpr(x)
expr_name(dots)
}
cement1.2( Hello )
# "Hello"
Upvotes: 1