首先,正如@Spacedman 所说,你会得到一个包裹的最佳服务,但还有其他选择。
S3 方法
R 的原始“面向对象”被称为 S3。R 的大部分代码库都使用这种特定的范例。它plot()
适用于各种对象。plot()
是一个通用函数,R 核心团队和包开发人员可以并且已经为plot()
. 严格来说,这些方法可能具有类似plot.foo()
wherefoo
是函数为其定义plot()
方法的对象类的名称。S3 的美妙之处在于,您(几乎)不需要知道或调用plot.foo()
您只是使用plot(bar)
,Rplot()
会根据 object 的类计算出要分派到哪个方法bar
。
在您对问题的评论中,您提到您有一个函数,该函数populate()
具有类的方法(有效),"crossvalidate"
并且"prod"
您将其保存在单独的.r
文件中。设置它的 S3 方法是:
populate <- function(x, ...) { ## add whatever args you want/need
UseMethod("populate")
}
populate.crossvalidate <-
function(x, y, z, ...) { ## add args but must those of generic
## function code here
}
populate.prod <-
function(x, y, z, ...) { ## add args but must have those of generic
## function code here
}
给定一些bar
带有类的对象"prod"
,调用
populate(bar)
将导致 R 调用populate()
(泛型),然后它会查找具有名称的函数,populate.prod
因为那是bar
. 它找到我们的populate.prod()
,然后调度该函数,将我们最初指定的参数传递给它。
所以你看到你只使用泛型名称引用方法,而不是完整的函数名称。R 为您计算出需要调用的方法。
这两种populate()
方法可以具有非常不同的参数,但严格来说它们应该具有与泛型函数相同的参数。所以在上面的例子中,所有方法都应该有参数x
和...
. (使用公式对象的方法有一个例外,但我们在这里不必担心。)
包命名空间
从 R 2.14.0 开始,所有 R 包都有自己的命名空间,即使包作者没有提供命名空间,尽管命名空间在 R 中的存在时间比这要长得多。
在您的示例中,我们希望注册populate()
泛型,它是 S3 系统的两种方法。我们还希望导出通用函数。通常我们不希望或不需要导出单个方法。因此,将您的函数弹出到包源.R
文件R
夹中的文件中,然后在包源的顶层创建一个名为NAMESPACE
并添加以下语句的文件:
export(populate) ## export generic
S3method(populate, crossvalidate) ## register methods
S3method(populate, prod)
然后,一旦你安装了你的包,你会注意到你可以调用populate()
,但是如果你尝试populate.prod()
从提示符或另一个函数中直接按名称调用 etc,R 会抱怨。这是因为作为单个方法的函数尚未从命名空间中导出,因此在命名空间之外是不可见的。包中调用的任何函数populate()
都可以访问您定义的方法,但包外的任何函数或代码根本看不到这些方法。如果需要,可以使用:::
运算符调用非导出函数,即
mypkg:::populate.crossvalidate(foo, bar)
将起作用,mypkg
您的包的名称在哪里。
老实说,您甚至不需要NAMESPACE
文件,因为 R 会在您安装软件包时自动生成一个文件,该文件会自动导出所有功能。这样,您的两种方法将显示为populate.xxx()
(xxx
特定方法在哪里)并将作为 S3 方法运行。
阅读编写 R 扩展手册中的第 1 节创建 R 包以了解所涉及的详细信息,但如果你不想这样做,你不需要做一半,特别是如果包是供你自己使用的。只需创建适当的包文件夹(即R
和),将文件man
粘贴到. 在您添加的位置写入单个文件.R
R
.Rd
man
\name{Misc Functions}
\alias{populate}
\alias{populate.crossvalidate}
\alias{populate.prod}
在文件的顶部。添加\alias{}
您拥有的任何其他功能。然后你需要构建和安装包。
替代使用sys.source()
虽然我不(不能!)真的推荐我在下面提到的作为长期可行的选择,但有一种替代方法可以让您.r
按照最初的要求将功能与单个文件隔离开来。这是通过使用环境而不是名称空间来实现的,并且不涉及创建包。
该sys.source()
函数可用于从.R
文件中获取 R 代码/函数并在环境中对其进行评估。当您.R
的文件正在创建/定义函数时,如果您在另一个环境中获取它,那么这些函数将在该环境中定义。默认情况下,它们在标准搜索路径上不可见,因此populate()
定义的函数crossvalidate.R
不会与populate()
定义的冲突prod.R
只要您使用两个不同的环境。当您需要使用一组功能时,您可以将环境分配给搜索路径,然后它将奇迹般地对所有内容可见,完成后您可以将其分离。附加其他环境,使用它,分离等。或者您可以安排在特定环境中使用诸如eval()
.
就像我说的那样,这不是推荐的解决方案,但它会按照您描述的方式工作。例如
## two source files that both define the same function
writeLines("populate <- function(x) 1:10", con = "crossvalidate.R")
writeLines("populate <- function(x) letters[1:10]", con = "prod.R")
## create two environments
crossvalidate <- new.env()
prod <- new.env()
## source the .R files into their respective environments
sys.source("crossvalidate.R", envir = crossvalidate)
sys.source("prod.R", envir = prod)
## show that there are no populates find-able on the search path
> ls()
[1] "crossvalidate" "prod"
> find("populate")
character(0)
现在,附加其中一个环境并调用populate()
:
> attach(crossvalidate)
> populate()
[1] 1 2 3 4 5 6 7 8 9 10
> detach(crossvalidate)
现在在另一个环境中调用该函数
> attach(prod)
> populate()
[1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j"
> detach(prod)
显然,每次你想使用一个特定的函数时,你都需要在attach()
它的环境中调用它,然后再调用它detach()
。这是一种痛苦。
我确实说过您可以安排在规定的环境中评估 R 代码(实际上是表达式)。例如,您可以使用eval()
of with()
。
> with(crossvalidate, populate())
[1] 1 2 3 4 5 6 7 8 9 10
至少现在您只需要一次调用即可运行populate()
您选择的版本。但是,如果用它们的全名来调用这些函数,例如populate.crossvalidate()
太费力(根据您的评论),那么我敢说即使是这个with()
想法也会太麻烦?无论如何,当您可以很容易地拥有自己的 R 包时,为什么还要使用它。