我R项目正在变得越来越复杂,我开始寻找一些构造,这相当于在Java/C#类,或Python模块,让自己的全局命名空间不会成为与那些从来外使用功能散落一个特定的.r文件.
所以,我想我的问题是:在多大程度上可以将函数的范围限制在特定的.r文件或类似文件中?
我想我可以将整个.r文件放到一个巨大的函数中,然后将函数放在其中,但这与回声混淆:
myfile.r:
myfile <- function() {
somefunction <- function(a,b,c){}
anotherfunction <- function(a,b,c){}
# do some stuff here...
123
456
# ...
}
myfile()
Run Code Online (Sandbox Code Playgroud)
输出:
> source("myfile.r",echo=T)
> myfile <- function() {
+ somefunction <- function(a,b,c){}
+ anotherfunction <- function(a,b,c){}
+
+ # do some stuff here...
+ # . .... [TRUNCATED]
> myfile()
>
Run Code Online (Sandbox Code Playgroud)
您可以看到"123"未打印,即使我们echo=T在source命令中使用过.
我想知道是否有一些更标准的其他构造,因为将所有内容放在单个函数中听起来不像是真正标准的东西?但也许是这样?此外,如果它意味着echo=T工作那么这对我来说是一个明确的奖金.
Rei*_*son 20
首先,正如@Spacedman所说,你将获得最佳服务,但还有其他选择.
R的原始"面向对象"被称为S3.R的大多数代码库都使用这种特殊的范例.它是使plot()各种物体工作的原因.plot()是一个通用函数,R核心团队和包开发人员可以编写自己的方法plot().严格地说,这些方法可能具有名称,例如plot.foo()where foo是一个对象类,函数为其定义plot()方法.S3的美妙之处在于你不需要(几乎)不需要知道或plot.foo()只是使用你,plot(bar)并且R plot()根据对象的类来确定调度哪种方法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
}
Run Code Online (Sandbox Code Playgroud)
给定一些bar带有类的对象"prod",调用
populate(bar)
Run Code Online (Sandbox Code Playgroud)
将导致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)
Run Code Online (Sandbox Code Playgroud)
然后,一旦你安装了你的软件包,你就会注意到你可以打电话populate()但如果你试图populate.prod()通过提示或其他功能直接通过名字来调用等,R会抱怨.这是因为单个方法的函数尚未从命名空间导出,因此在其外部不可见.您调用的包中的任何函数populate()都可以访问您定义的方法,但包外的任何函数或代码都无法查看方法.如果需要,可以使用:::运算符调用非导出函数,即
mypkg:::populate.crossvalidate(foo, bar)
Run Code Online (Sandbox Code Playgroud)
将工作,mypkg你的包的名称在哪里.
说实话,你甚至不需要一个NAMESPACE文件,因为R会在你安装软件包时自动生成一个文件,它会自动导出所有的功能.这样你的两个方法将可见populate.xxx()(xxx特定方法在哪里)并将作为S3方法运行.
请阅读"编写R扩展"手册中的第1节"创建R包"以获取所涉及的内容的详细信息,但如果您不需要,则yuo不需要执行此操作的一半,尤其是如果包是供您自己使用的话.只需创建适当的包文件夹(即R和man),粘贴您的.R文件R.在您添加的位置写一个.Rd文件man
\name{Misc Functions}
\alias{populate}
\alias{populate.crossvalidate}
\alias{populate.prod}
Run Code Online (Sandbox Code Playgroud)
在文件的顶部.添加\alias{}您拥有的任何其他功能.然后你需要构建和安装包.
sys.source()虽然我不(不能!)真的推荐我在下面提到的作为长期可行的选项,但是有一个替代方案可以让你根据.r最初的要求将函数与单个文件隔离开来.这是通过使用非命名空间的环境来实现的,并且不涉及创建包.
该sys.source()函数可用于从.R文件中获取R代码/函数,并在环境中对其进行评估.由于您.R正在创建/定义函数,如果您在另一个环境中获取它,那么将在那个环境中定义那些函数.默认情况下,它们在标准搜索路径中不可见,因此populate()定义的函数crossvalidate.R不会与populate()定义的内容冲突prod.R只要你使用两个独立的环境.当您需要使用一组功能时,您可以将环境分配给搜索路径,然后在其中奇迹般地可以看到所有内容,当您完成后,您可以将其分离.附加其他环境,使用它,分离等.或者您可以使用类似的东西安排在特定环境中评估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)
Run Code Online (Sandbox Code Playgroud)
现在,附加其中一个环境并调用populate():
> attach(crossvalidate)
> populate()
[1] 1 2 3 4 5 6 7 8 9 10
> detach(crossvalidate)
Run Code Online (Sandbox Code Playgroud)
现在在其他环境中调用该函数
> attach(prod)
> populate()
[1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j"
> detach(prod)
Run Code Online (Sandbox Code Playgroud)
显然,每次要使用特定功能时,都需要attach()对其环境进行调用,然后调用它detach().这是一种痛苦.
我确实说过你可以安排在规定的环境中评估R代码(表达式).您可以使用eval()的with()这个例如.
> with(crossvalidate, populate())
[1] 1 2 3 4 5 6 7 8 9 10
Run Code Online (Sandbox Code Playgroud)
至少现在您只需要一次调用即可运行populate()您选择的版本.但是,如果用他们的全名调用这些函数,例如populate.crossvalidate()是太多的努力(根据你的评论)那么我敢说即使这个with()想法也会太麻烦?无论如何,当你可以很容易地拥有自己的R包时,为什么要使用它呢?
Spa*_*man 14
不要担心"制作包装"的复杂性.不要那样想.你要做的是:
install.packages("devtools")library(devtools)现在,将您的函数写入R文件夹中的R文件中.要将它们加载到R中,DONT来源它们.做load_all().您的函数将被加载但不会加载到全局环境中.
编辑其中一个R文件,然后load_all()再次执行.这将加载R文件夹中的任何已修改文件,从而更新您的功能.
而已.编辑,load_all冲洗并重复.你已经创建了一个包,但它非常轻巧,你不必处理R的包构建工具的束缚和纪律.
我已经看过,使用过,甚至编写了试图实现轻量级包装机制来加载对象的代码,没有一个能像devtools那样好.
所有冰雹哈德利!