Hi all,
I would like to access the name of a variable passed to an S4 method.
For a function, I would do this:
f <- function(x) as.character(substitute(x))
This also works for a the examples I have tried for methods that do
not have extra, non-dispatch args:
setGeneric("A", function(x, ...) standardGeneric("A"))
setMethod("A", signature(x="character"),
function(x) as.character(substitute(x)))
However, I'm seeing strange behavior if the method uses an extra
argument:
setMethod("A", signature(x="numeric"),
function(x, y) as.character(substitute(x)))
num <- 1
A(num)
[1] "x"
A(num, 2)
[1] "x"
Is there a way to make this work? I came up with one workaround that
uses a non-standard generic (see below).
It seems that when a method uses extra args matching '...' in the
generic, an extra frame is used in the evaluation and so substitute()
isn't reaching the same place as without extra args.
Thanks in advance for pointers to doc or suggestions.
+ seth
## here is a non-standard generic that gives me the behavior I want
setGeneric("B", function(x, ...) {
x.name <- as.character(substitute(x))
standardGeneric("B")
})
setMethod("B", signature(x="character"),
function(x, y) {
penv <- sys.frames()
penv <- penv[[length(penv)-2]]
get("x.name", envir=penv)
})
Observation: Without an extra arg in the method, the appropriate
environment would be penv[[length(penv) - 1]], but the presence of the
extra arg results in an extra environment in the evaluation, hence we
need -2.
Try defining your method like this. I don't know how generally this
works but it seems to work here.
setMethod("A", signature(x="numeric"),
function(x, y) as.character(substitute(x, sys.frame(-1))))
On 1/25/06, Seth Falcon <sfalcon at fhcrc.org>
wrote:> Hi all,
>
> I would like to access the name of a variable passed to an S4 method.
> For a function, I would do this:
>
> f <- function(x) as.character(substitute(x))
>
> This also works for a the examples I have tried for methods that do
> not have extra, non-dispatch args:
>
> setGeneric("A", function(x, ...)
standardGeneric("A"))
>
> setMethod("A", signature(x="character"),
> function(x) as.character(substitute(x)))
>
> However, I'm seeing strange behavior if the method uses an extra
> argument:
>
> setMethod("A", signature(x="numeric"),
> function(x, y) as.character(substitute(x)))
>
> num <- 1
>
> A(num)
> [1] "x"
>
> A(num, 2)
> [1] "x"
>
> Is there a way to make this work? I came up with one workaround that
> uses a non-standard generic (see below).
>
> It seems that when a method uses extra args matching '...' in the
> generic, an extra frame is used in the evaluation and so substitute()
> isn't reaching the same place as without extra args.
>
> Thanks in advance for pointers to doc or suggestions.
>
>
> + seth
>
> ## here is a non-standard generic that gives me the behavior I want
>
> setGeneric("B", function(x, ...) {
> x.name <- as.character(substitute(x))
> standardGeneric("B")
> })
>
> setMethod("B", signature(x="character"),
> function(x, y) {
> penv <- sys.frames()
> penv <- penv[[length(penv)-2]]
> get("x.name", envir=penv)
> })
>
> Observation: Without an extra arg in the method, the appropriate
> environment would be penv[[length(penv) - 1]], but the presence of the
> extra arg results in an extra environment in the evaluation, hence we
> need -2.
>
> ______________________________________________
> R-devel at r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel
>
On Wed, 25 Jan 2006, Seth Falcon wrote:> I would like to access the name of a variable passed to an S4 method. > For a function, I would do this: > > f <- function(x) as.character(substitute(x)) > > This also works for a the examples I have tried for methods that do > not have extra, non-dispatch args: > > setGeneric("A", function(x, ...) standardGeneric("A")) > > setMethod("A", signature(x="character"), > function(x) as.character(substitute(x))) > > However, I'm seeing strange behavior if the method uses an extra > argument: > > setMethod("A", signature(x="numeric"), > function(x, y) as.character(substitute(x))) > > num <- 1 > > A(num) > [1] "x" > > A(num, 2) > [1] "x" > > Is there a way to make this work? I came up with one workaround that > uses a non-standard generic (see below). > > It seems that when a method uses extra args matching '...' in the > generic, an extra frame is used in the evaluation and so substitute() > isn't reaching the same place as without extra args.The reason you get an extra frame is that when the method's argument doesn't match the generic's, setMethod makes a function with the generic's argument list that calls your method (renamed ".local") and makes that new function the real method. E.g., > setMethod("A",sig=signature(x="character"), function(x,n){ if(nchar(x)>n) stop("nchar(x)>n") deparse(substitute(x)) }) [1] "A" > getMethod("A",sig=signature(x="character")) Method Definition: function (x, ...) { .local <- function (x, n) { if (nchar(x) > n) stop("nchar(x)>n") deparse(substitute(x)) } .local(x, ...) } ... This has 2 bothersome side effects. One is yours: > A(paste("One","Two"), 10) [1] "x" and the other is that the function mentioned in the error report is misleading: > A("xyz", 1) Error in .local(x, ...) : nchar(x)>n You can workaround both problems by making a method that looks somewhat like the the one generated by setMethod but gets some details right for your function. E.g., > setMethod("A",sig=signature(x="character"), function(x, ...) { A.character <- function(x,n,x.name){ if(nchar(x)>n) stop("nchar(x)>n") x.name } x.name <- deparse(substitute(x)) A.character(x, ..., x.name=x.name) } ) This gives a suggestive function name in the error message > A(paste("One","Two"), 3) Error in A.character(x, ..., x.name = x.name) : nchar(x)>n and lets you use substitute: > A(paste("One","Two"), 10) [1] "paste(\"One\", \"Two\")" Thus you don't have to guess how many frames or environments lie between your method and the generic. This works in R and Splus. ---------------------------------------------------------------------------- Bill Dunlap Insightful Corporation bill at insightful dot com 360-428-8146 "All statements in this message represent the opinions of the author and do not necessarily reflect Insightful Corporation policy or position."