代码之家  ›  专栏  ›  技术社区  ›  Justin Landis

r:s3根据参数进行方法分派

  •  2
  • Justin Landis  · 技术社区  · 8 年前

    我有一个通用函数 foo 我想根据给它的参数调用三种不同的方法。

    foo <- function(...) UseMethod("foo")
    
    #default
    foo.default <- function(x, y, ...) {
    #does some magic
    print("this is the default method")
    }
    
    #formula
    foo.formula <- function(formula, data = list(), ...) {
    print("this is the formula method")
    }
    
    #data.frame
    foo.data.frame <- function(data, x, y, ...) {
    print("this is the data.frame method")
    }
    

    在下面我将展示我是怎样的 期望 方法分派到工作,但输出在每次调用时都会显示…

    mydata <- data.frame(x=c(1,2,3,4),y=c(5,6,7,8))
    
    #ways to call default function
    foo(x = mydata$x, y = mydata$y)
    #[1] "this is the default method"
    
    #ways to call formula
    foo(formula = mydata$x~mydata$y)
    #[1] "this is the formula method"
    foo(formula = x~y, data = mydata)
    #[1] "this is the formula method"
    foo(data = mydata, formula = x~y)  #ERROR
    #[1] "this is the data.frame method"
    
    #ways to call data.frame method
    foo(data = mydata, x = x, y = y)
    #[1] "this is the data.frame method"
    foo(x = x, y = y, data = mydata) #ERROR
    #Error in foo(x = x, y = y, data = mydata) : object 'x' not found
    

    据我所知,使用的方法取决于第一个参数的类。本质上,我希望将方法分派到 取决于传递的参数 到泛型函数 不是第一个论点 .

    我希望派遣具有以下优先权:

    如果存在公式参数,则使用公式方法(此处的数据参数应为可选参数)

    然后,如果找不到公式参数,如果存在数据参数,请使用data.frame方法(它需要x和y参数)

    其他的 需要x和y参数,否则它将失败。

    注释

    我想避免定义泛型函数 如下所示

    foo <- function(formula, data,...) UseMethod("foo")
    

    虽然这可以解决我的所有问题(我相信除了最后一个案例之外的所有问题),但这将导致 devtools::check() 警告,因为S3函数中的一些将不会具有与泛型函数相同的参数,并且不再是一致的(具体是Fo.Debug和Foo.Diga.Frand)。我不想包含缺少的参数,因为这些方法没有用于这些参数。

    1 回复  |  直到 8 年前
        1
  •  0
  •   coffeinjunky    8 年前

    正如thomas所指出的,这不是s3类的标准行为。但是,如果您真的想坚持使用s3,可以编写函数来“模仿” UseMethod ,即使它不漂亮,也可能不是你想做的。然而,这里的想法是基于先捕获所有参数,然后检查是否存在“首选”参数类型:

    首先获取一些对象:

    a <- 1; class(a) <- "Americano"
    b <- 2; class(b) <- "Espresso"
    

    让所讨论的函数捕获所有带点的参数,然后按您的偏好顺序检查是否存在参数类型:

    drink <- function(...){
      dots <- list(...)
    
      if(any(sapply(dots, function(cup) class(cup)=="Americano"))){
        drink.Americano(...)
        } else { # you can add more checks here to get a hierarchy
            # try to find appropriate method first if one exists, 
            # using the first element of the arguments as usual
            tryCatch(get(paste0("drink.", class(dots[[1]])))(), 
            # if no appropriate method is found, try the default method:
                 error = function(e) drink.default(...)) 
      }
    }
    
    drink.Americano <- function(...) print("Hmm, gimme more!")
    drink.Espresso <- function(...) print("Tripple, please!")
    drink.default <- function(...) print("Any caffeine in there?")
    
    drink(a) # "Americano", dispatch hard-coded.
    # [1] "Hmm, gimme more!"
    drink(b) # "Espresso", not hard-coded, but correct dispatch anyway
    # [1] "Tripple, please!"
    drink("sthelse") # Dispatches to default method
    # [1] "Any caffeine in there?"
    drink(a,b,"c")
    # [1] "Hmm, gimme more!"
    drink(b,"c", a)
    # [1] "Hmm, gimme more!"