当需要不同的参数时,S3通用/方法一致性

4

我已经阅读了几个SO答案,但仍然无法拼凑出S3方法在具有不同参数时需要如何记录文档的方式。在下面的示例中,我希望用户能够输入两个对象或将它们作为列表传入。代码运行良好,只是会输出关于方法一致性的警告。我尝试移动list参数,但总是得到一些警告的组合,要么是foo != foo.list,要么是foo != 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帖子:


1
这里你试图实现的行为并不清楚。S3分发仅适用于第一个参数的class()。你似乎想要根据参数数量进行分发,但S3无法做到。所有的foo函数需要与通用版本具有相同的参数,直到...为止。它们可以在之后拥有不同的参数。 - MrFlick
谢谢,我之前不知道关于...的事情。我遇到的问题是在RStudio中无法显示帮助信息。如果我只在通用函数中放置...,那么函数可以正常工作,但是它不会通过IntelliSense(自动完成)显示任何内容。用户通常会传递单个值x,然后将其与y组合,但是如果用户输入列表,则希望函数从列表中获取值。最好的方法是如何记录此信息以便IntelliSense能够识别? - yake84
2个回答

3

我不确定在这里是否会使用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)

一个例子是在基础功能中的plot函数.

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

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

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


1
如@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]])

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接