
嗨,habrozhiteli! 许多用户使用R来执行特定任务-在此处构建直方图,在此处进行回归分析或执行与统计数据处理相关的其他单独操作。 但是,这本书是为那些想要在R中开发软件的人而写的。本书的目标读者的编程技能可以从专业资格到“我在大学学习过编程课程”不等,但关键是为特定目的编写R代码。 。 (通常不需要全面了解统计信息。)
可能从本书中受益的一些读者示例:
- 必须定期发布统计报告并为此制定程序的分析人员(例如,在医院或政府机构中工作)。
- 参与统计方法开发的科学家-新方法或将现有方法集成到集成程序中。 需要对方法进行编码,以便可以在研究社区中使用。
- 市场营销,法律支持,新闻,出版等方面的专家,参与了用于构建数据的复杂图形表示的代码的开发。
- 具有软件开发经验的专业程序员,负责与统计分析相关的项目。
- 学生学习统计和数据处理。
因此,这本书并不是对R软件包的无数统计方法的引用。实际上,它致力于编程,并且处理了其他R本书中很少见的编程问题。甚至从编程的角度考虑了基础主题。 此方法的一些示例:
- 本书包含“高级示例”的各个部分。 通常,它们提供完整的通用功能,而不是基于特定数据的孤立代码段。 此外,其中的某些功能可能会在R的日常工作中派上用场。通过研究这些示例,您不仅将了解特定的R构造如何工作,而且还将学习如何将它们组合为有用的程序。 在许多情况下,我会提供替代解决方案的描述并回答以下问题:“为什么这样做?”
- 提出材料时要考虑到程序员的看法。 例如,当描述数据帧时,我不仅声称R中的数据帧是一个列表,而且还从编程的角度指出了这一事实的后果。 同样,在文本中,R与其他可能有用的语言(对于说这些语言的读者)进行了比较。
- 调试在任何语言的编程中都起着至关重要的作用,但是有关R的大多数书籍都没有提到这个主题。 在本书中,我整整一章专门介绍调试工具,使用了“高级示例”的原理,并介绍了如何在现实中调试程序的完整开发演示。
- 如今,多核计算机已经出现在所有家庭中,图形处理器(GPU)的编程正在科学计算领域掀起一场令人难以察觉的革命。 越来越多的R应用程序需要大量的计算,并且并行处理已成为R程序员的重要工作,整本书专门讨论该主题,除了描述机制外,还给出了高级示例。
- 单独的章节讨论了如何使用有关R的内部实现和其他方面的信息来加快R代码的工作。
- 其中一章重点介绍R与其他编程语言(例如C和Python)的接口。 再次特别注意高级示例和调试建议。
摘录。 7.8.4。 什么时候应该使用全局变量?
在程序员社区中,关于使用全局变量尚无共识。 显然,此部分标题中的问题没有正确答案,因为这是个人喜好和风格的问题。 不过,许多程序员认为,许多编程老师所主张的全面禁止全局变量将是不必要的强硬措施。 在本节中,我们研究了在R.结构的上下文中全局变量可能带来的好处,术语“全局变量”是指环境层次中高于所关注代码级别的任何变量。
在R中使用全局变量比您预期的更普遍。 令人惊讶的是,R在其内部实现中广泛使用了全局变量(在C代码和R函数中)。 因此,超级赋值运算符<<-在R的许多库函数中使用(尽管通常用于写入仅位于变量层次结构中较高一级的变量)。 用于编写快速程序的多线程代码和GPU代码(请参阅第16章)通常使用全局变量,这些变量提供了并行执行程序之间交互的主要机制。
现在,为具体起见,让我们回到第7.7节中的早期示例:
f <- function(lxxyy) { # lxxyy — , x y ... lxxyy$x <- ... lxxyy$y <- ... return(lxxyy) } # x y lxy$x <- ... lxy$y <- ... lxy <- f(lxy) # x y ... <- lxy$x ... <- lxy$y
如前所述,此代码可能变得很麻烦,特别是如果x和y本身是列表。
另一方面,请看一下使用全局变量的替代方案:
f <- function() { ... x <<- ... y <<- ... } # x y x <-... y <-... f() # x y # x y ... <- x ... <- y
也许第二个版本更干净,体积更小且不需要列表操作。 清晰的代码通常会减少编写,调试和维护方面的问题。
由于这些原因-为了简化和减少代码的体积-我们决定使用全局变量,而不是返回前面给出的DES代码中的列表。 更详细地考虑此示例。
使用了两个全局变量(都是包含不同信息的列表):sim变量与库代码相关联,mm1glbls变量与特定的应用程序代码M / M / 1相关联。 让我们从sim开始。
即使是对全局变量有所约束的程序员也同意,如果它们确实是全局的,则可以合理地使用此类变量,因为它们在程序中得到了广泛使用。 所有这些都与DES示例中的sim变量有关:在库代码(在schedevnt(),getnextevnt()和dosim()中)和M / M / 1代码(在mm1reactevnt()中)中都使用了它。 在此特定示例中,对sim的后续调用仅限于读取,但在某些情况下可以进行记录。 这种类型的典型示例是事件取消的可能实现。 例如,当对“两个中的较早者”原理建模时,可能会发生这种情况:计划了两个事件,并且当其中一个发生时,另一个应取消。
因此,使用sim作为全局变量似乎是合理的。 但是,如果我们坚决拒绝使用全局变量,则可以将sim放在dosim()中的局部变量中。 该函数将在上一段中提到的所有函数(schedevnt(),getnextevnt()等)的参数中传递sim,并且每个函数都将返回修改后的sim变量。
例如,第94行:
reactevnt(head)
转换为以下形式:
sim <- reactevnt(head)
之后,应将以下行添加到与特定应用程序关联的mm1reactevnt()函数中:
return(sim)
您可以通过在dosim()中包含名称为(例如)appvars的局部变量来与mm1glbls做类似的事情。 但是,如果使用两个变量来完成此操作,则必须将它们放在列表中,以便可以从函数中返回两个变量,就像上面的f()函数示例一样。 然后出现了上面提到的列表内部列表的庞大结构,或者更确切地说,是列表内部列表内部的列表。
另一方面,反对使用全局变量的人注意到代码简单性是徒劳的。 他们担心在调试过程中很难找到全局变量更改值的位置,因为更改可能发生在程序中的任何位置。 看起来,在现代文本编辑器和集成开发工具将帮助发现所有变量出现的世界中,问题就顺其自然了(敦促放弃使用全局变量的原始文章于1970年发表!)。 但是,必须考虑到这一因素。
当从具有不同值的程序的几个不相关部分调用函数时,会遇到评论家提到的另一个问题。 例如,假设函数f()是从程序的不同部分调用的,则每次调用都接收自己的x和y值,而不是每个值一个。 通过创建x和y值的向量可以解决该问题,其中程序中f()的每个实例都有一个单独的元素。 但是,这将失去使用全局变量的简单性。
这些问题不仅在R中遇到,而且在更一般的上下文中也遇到。 但是,在R中,较高级别的全局变量的使用会带来另一个问题,因为该级别的用户通常具有许多变量。 使用全局变量的代码可能会意外替换具有相同名称的完全无关的变量。
当然,这个问题很容易解决-为绑定到特定应用程序的全局变量选择长名称就足够了。 但是,环境也提供了合理的折衷方案,例如以下情况下的DES示例。
在dosim()函数内部,该行
sim <<- list()
可以用字符串代替
assign("simenv",new.env(),envir=.GlobalEnv)
它会在顶层创建simenv变量引用的新环境。 该环境用作封装全局变量的容器,可以通过调用get()和assign()来访问这些全局变量。 例如,字符串
if (is.null(sim$evnts)) { sim$evnts <<- newevnt
在schedevnt()中采用以下形式
if (is.null(get("evnts",envir=simenv))) { assign("evnts",newevnt,envir=simenv)
是的,此解决方案也很麻烦,但至少它不像列表内列表中的列表那样复杂。 而且它可以防止意外写入顶层的无关变量。 使用超级分配运算符仍然会减少代码的麻烦,但是应该考虑这一折衷。
像往常一样,没有一种编程风格可以在所有情况下提供最佳结果。 带有全局变量的解决方案是应该包含在编程工具库中的另一个选项。
7.8.5。 短路
让我提醒您,R的闭包由参数以及函数的主体以及调用时的环境组成。 启用环境的事实涉及编程范例,该范例使用该概念(也称为闭包)(此处存在一些术语重载)。
闭包是一个创建局部变量,然后创建另一个访问此变量的函数的函数。 描述太抽象了,所以我最好举个例子。
1 > counter 2 function () { 3 ctr <- 0 4 f <- function() { 5 ctr <<- ctr + 1 6 cat("this count currently has value",ctr,"\n") 7 } 8 return(f) 9 }
在深入研究实现细节之前,让我们检查一下这段代码是如何工作的:
> c1 <- counter() > c2 <- counter() > c1 function() { ctr <<- ctr + 1 cat("this count currently has value",ctr,"\n") } <environment: 0x8d445c0> > c2 function() { ctr <<- ctr + 1 cat("this count currently has value",ctr,"\n") } <environment: 0x8d447d4> > c1() this count currently has value 1 > c1() this count currently has value 2 > c2() this count currently has value 1 > c2() this count currently has value 2 > c2() this count currently has value 3 > c1() this count currently has value 3
在这里,counter()函数被调用两次,并将结果分配给c1和c2。 不出所料,这两个变量由函数组成,即f()的副本。 但是,f()通过超级分配运算符访问ctr变量,并且该变量将是在counter()本地具有指定名称的变量,因为它将是环境层次结构中路径上的第一个变量。 它是环境f()的一部分,因此被包装到返回counter()的调用方的内容中。 关键点是,通过对counter()的不同调用,ctr变量将位于不同的环境中(在示例环境中,它存储在内存中的地址0x8d445c0和0x8d447d4处)。 换句话说,对counter()的不同调用将创建物理上不同的ctr实例。
结果,函数c1()和c2()作为完全独立的计数器工作。 从可以多次调用每个函数的示例中可以看出。
»这本书的更多信息可以
在出版商的网站上找到»
目录»
摘录小贩优惠券25%折扣
-R支付纸质版本的书后,将通过电子邮件发送该书的电子版本。