R中的引用调用(使用函数修改对象)

Mik*_*yth 5 r function

我只是在R中弄湿了,并且惊讶地看到一个函数没有修改一个对象,至少它似乎是默认的.例如,我写了一个函数只是为了在表中的一个标签上粘贴一个星号; 它在函数内部工作,但表本身不会更改.(我主要来自Ruby)

那么,使用函数来改变R中对象的正常,可接受的方式是什么?如何在表格标题中添加星号?

  • 替换整个对象: myTable = title.asterisk(myTable)

  • 使用解决方法通过引用进行调用(例如,在TszKin Julian的R中通过引用调用中所描述的?

  • 使用函数以外的一些结构?对象方法?

Din*_*nre 22

您遇到麻烦的原因是您将对象传递到函数的本地命名空间.这是关于R的伟大/可怕的事情之一:它允许隐式变量声明,然后在命名空间变得更深时实现超越.

这会影响您,因为函数在当前命名空间中创建新的命名空间.我认为,对象'myTable'最初是在全局命名空间中创建的,但是当它传递给函数'title.asterisk'时,新的函数本地命名空间现在有一个具有相同属性的对象.这样工作如下:

title.asterisk <- function(myTable){ do some stuff to 'myTable' }
Run Code Online (Sandbox Code Playgroud)

在这种情况下,函数'title.asterisk'不会对全局对象'myTable'进行任何更改.而是使用相同的名称创建本地对象,因此本地对象取代全局对象.如果我们title.asterisk(myTable)以这种方式调用函数,该函数仅对局部变量进行更改.

有两种直接的方法来修改全局对象(以及许多间接方式).

选项1:首先,如您所述,让函数返回对象并覆盖全局对象,如下所示:

title.asterisk <- function(myTable){
    do some stuff to 'myTable'
    return(myTable)
}
myTable <- title.asterisk(myTable)
Run Code Online (Sandbox Code Playgroud)

这没关系,但你仍然使你的代码有点难以理解,因为实际上有两个不同的'myTable'对象,一个是全局的,一个是函数的局部对象.许多程序员通过添加句号''来清除这一点.在变量参数前面,如下所示:

title.asterisk <- function(.myTable){
    do some stuff to '.myTable'
    return(.myTable)
}
myTable <- title.asterisk(myTable)
Run Code Online (Sandbox Code Playgroud)

好的,现在我们有一个视觉提示,两个变量是不同的.这很好,因为当我们稍后尝试调试我们的代码时,我们不想依赖名称空间超越等不可见的东西.它只会使事情变得更加艰难.

选项2:您可以从函数中修改对象.当您想对对象进行破坏性编辑并且不希望内存膨胀时,这是更好的选择.如果您正在进行破坏性编辑,则无需保存原始副本.此外,如果您的对象适当大,您不希望在不需要时复制它.要编辑全局命名空间对象,只需将其传递给函数或从函数中声明它.

title.asterisk <- function(){ do some stuff to 'myTable' }
Run Code Online (Sandbox Code Playgroud)

现在我们从函数中直接编辑对象'myTable'.我们没有传递对象的事实使我们的函数看起来更高级别的命名空间来尝试解析变量名称.瞧,它发现一个'myTable'对象更高了!函数中的代码对对象进行更改.

要考虑的注意事项:我讨厌调试.我的意思是我真的讨厌调试.这对我来说意味着一些事情:

  • 我几乎把所有东西都包在一个函数中 当我编写代码时,只要我得到一个工作,我将它包装在一个函数中并将其放在一边.我大量使用'.' 我的所有函数参数的前缀,并且对于它所存在的命名空间本机的任何内容都不使用前缀.
  • 我尽量不从函数内修改全局对象.我不喜欢这导致的地方.如果需要修改对象,我会从声明它的函数中修改它.这通常意味着我有多个函数调用函数,但它使我的工作既模块化又易于理解.
  • 我评论了我的所有代码,解释了每行或每个块的用途.它可能看起来有点无关,但我发现这三件事对我来说都是一致的.一旦开始在函数中包装编码,您将发现自己想要重用更多的旧代码.这就是优秀评论的来源.对我来说,这是必要的一部分.

  • 在帖子结尾处的明智评论 - 绝对值得+1. (2认同)

Mar*_*gan 10

正如你所指出的那样,这两种范式正在取代整个对象,或者写出"替换"函数,例如

`updt<-` <- function(x, ..., value) {
    ## x is the object to be manipulated, value the object to be assigned
    x$lbl <- paste0(x$lbl, value)
    x
}
Run Code Online (Sandbox Code Playgroud)

> d <- data.frame(x=1:5, lbl=letters[1:5])
> d
  x lbl
1 1   a
2 2   b
3 3   c
> updt(d) <- "*"
> d
  x lbl
1 1  a*
2 2  b*
3 3  c*
Run Code Online (Sandbox Code Playgroud)

例如,这是对$<-就地访问的元素进行就地更新的行为$.是一个相关的问题.人们可以将替代函数视为语法糖

updt1 <- function(x, ..., value) {
    x$lbl <- paste0(x$lbl, value)
    x
}
d <- updt1(d, value="*")
Run Code Online (Sandbox Code Playgroud)

但是在我看来,标签"语法糖"对于所涉及的核心范式并没有真正公正.它实现了方便的就地更新,这与R通常维护的更改时的错觉不同,它实际上是更新对象的"R"方式(而不是使用?ReferenceClasses,例如,更多的更新对象)其他语言的感觉,但会让R用户期待复制更改语义.