【问题标题】:r - S3 generic/method consistency when different arguments neededr - 需要不同参数时的 S3 泛型/方法一致性
【发布时间】:2021-10-12 02:01:28
【问题描述】:

我已经阅读了几个 SO 答案,但仍然无法拼凑出在 S3 方法有不同参数时需要如何记录它们。在下面的示例中,我希望用户能够在列表中输入两个对象或管道。代码工作正常。它只是发出关于方法一致性的警告。我试过移动list参数,但总是得到foo!=foo.listfoo!=foo.default的警告组合。在折叠的代码部分和末尾的相关链接中有一个虚拟表示。谢谢!

#' does some stuff
#' @param list for list methods
#' @param x arg1
#' @param y arg2
#' @param ... some argument
#' @export
foo <- function(list, x, y, ...) UseMethod('foo')

#' @export
foo.default <- function(x, y, ...) paste(x, y, ...)

#' do stuff for lists
#' @export
#' @param list for list methods
#' @inheritParams foo
foo.list <- function(list, x, y, ...) foo(list[[x]], list[[y]])

# create dummy package
tmp <- tempdir() 
setwd(tmp)
devtools::create(path = "test")
setwd("test")
usethis::use_mit_license()

# add R code
writeLines(
  con = "R/test.R",
  text = 
"#' does some stuff
#' @param list for list methods
#' @param x arg1
#' @param y arg2
#' @param ... some argument
#' @export
foo <- function(list, x, y, ...) UseMethod('foo')

#' @export
foo.default <- function(x, y, ...) paste(x, y, ...)

#' do stuff for lists
#' @export
#' @param list for list methods
#' @inheritParams foo
foo.list <- function(list, x, y, ...) foo(list[[x]], list[[y]])"
)

devtools::document()
devtools::load_all()

# examples
foo(1, 2)
list(a = 1, b = 2) |> foo("a", "b")

# check
devtools::check(document = FALSE)
 W  checking S3 generic/method consistency (561ms)
 foo:
   function(list, x, y, ...)
     foo.default:
   function(x, y, ...)
     
     See section 'Generic functions and methods' in the 'Writing R
    Extensions' manual.

以下是一些相关的 SO 帖子:

【问题讨论】:

  • 目前尚不清楚您要在这里实现什么行为。 S3 调度仅适用于第一个参数的class()。您似乎想根据 S3 无法执行的参数数量进行调度。您的所有foo 函数都需要具有与通用版本相同的参数,直到...。之后它们可以有不同的参数。
  • 谢谢,我不知道... 我遇到的问题是在 RStudio 中获得帮助。如果我只将... 放在泛型中,则功能可以正常工作,但不会通过 IntelliSense(自动完成)显示任何内容。用户通常会传递单个值x,然后将其与y 组合,但如果用户在列表中使用管道,我希望函数从列表中获取值。记录此内容以便 IntelliSense 识别的最佳方法是什么?

标签: r devtools


【解决方案1】:

我不确定我会在这里使用 S3。不回答你的具体问题,但考虑这样的事情。当然,如果您的实际用例与您的示例相距甚远,这无关紧要。

foo <- function(x, y) {
  if (is.list(x)) return(do.call(foo, x))
   
  paste(x, y)
}

所以现在您可以获得您想要的行为,只需将x 记录为参数列表或第一个参数。否则,您将创建一些烦人的事情,例如要求用户每次都明确命名参数。

foo("a", "b")
# [1] "a b"

foo(list(x = "a", y = "b"))
# [1] "a b"

foo(list(y = "a", x = "b"))
# [1] "b a"

foo("a", "b", z = "c")
# Error in foo("a", "b", z = "c") : unused argument (z = "c")

foo(list("a", "b", z = "c"))
# Error in (function (x, y)  : unused argument (z = "c")

如果必须,您可以使用 S3 完成同样的事情。但无论哪种方式,您可能只想将参数命名为同一事物并将其记录为具有两种含义。

foo <- function(x, y, ...) UseMethod('foo')

foo.default <- function(x, y, ...) paste(x, y)

foo.list <- function(x, y, ...) do.call(foo, x)

base 中的 plot 函数就是一个例子。

x 图中点的坐标。或者,可以提供单个绘图结构、函数或任何具有绘图方法的 R 对象。

y 图中点的 y 坐标,如果 x 是适当的结构,则为可选。

这里,x 是 x 坐标一个有效的绘图结构。然后y 仅在需要时使用。

【讨论】:

    【解决方案2】:

    正如@MrFlick 提到的,如果您想要一个基于参数数量分派的 S3 方法,则无法做到这一点。我不确定您希望 IntelliSense 如何显示参数,但也许您可以只从一个参数开始?

    #' does some stuff
    #' @param x argument 1, could be a list
    #' @param \dots Further arguments passed to each method
    #' @export
    foo <- function(x, ...) UseMethod('foo', x)
    
    #' @export
    foo.default <- function(x, ...) paste(x, ...)
    
    #' do stuff for lists
    #' @export
    #' @inheritParams foo
    #' @param a an element of x
    #' @param b another element of x
    foo.list <- function(x, a, b, ...) foo(x[[a]], x[[b]])
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-02
      • 2015-09-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多