R中的点号是什么意思 - 个人偏好、命名约定还是其他?

95

我(可能)并不是指“所有其他变量”这样的意思,比如var1~.。有一次,我被引导使用了plyr,然后研究了一下mlply,想知道为什么参数是这样定义的:

function (.data, .fun = NULL, ..., .expand = TRUE, .progress = "none", 
.parallel = FALSE) 
{
if (is.matrix(.data) & !is.list(.data)) 
    .data <- .matrix_to_df(.data)
f <- splat(.fun)
alply(.data = .data, .margins = 1, .fun = f, ..., .expand = .expand, 
    .progress = .progress, .parallel = .parallel)
}
<environment: namespace:plyr>

那有什么用呢?它只是个人喜好、命名约定还是更多?常常情况下R语言很实用,但我可能会错过一些长期以来一直在使用的技巧。


6
好问题。在其他编程语言中,函数名称中使用点号(is.naas.data.frame,...)并不常见,但我喜欢这样做。 - Tomas
进一步解释:http://stats.stackexchange.com/questions/10712/what-is-the-meaning-of-the-dot-in-r - leo9r
purrr包(purrr.tidyverse.org)现在添加了另一层含义,例如~ .x+1 == function(x) x+1 - isomorphismes
1
如果键盘上的“_”键放在一个容易到达的位置(比如,你知道的,而不是一个“购物”键...),那么这种混淆就可以得到解决。 - isomorphismes
3个回答

137

函数名中的点(.)可以表示以下任意一种含义:

  • 什么也不表示
  • 在S3方法中,用于分隔方法和类
  • 隐藏函数名

可能的含义

1. 什么也不表示

data.frame 函数名中的点并没有将 dataframe 分开,除了视觉上的区分。

2. 在 S3 方法中分离方法和类

plot 是一个通用的 S3 方法的例子。因此,plot.lmplot.glm 是在调用 plot(lm(...))plot(glm(...)) 时使用的底层函数定义。

3. 隐藏内部函数

在编写包时,有时在函数名中使用前导点是有用的,因为这些函数在一定程度上被隐藏在一般视图之外。有时使用这些函数是为了纯粹作为包内部使用的。

在这种情况下,“有些隐藏”只是意味着该变量(或函数)通常不会在使用 ls() 列出对象时显示。要强制 ls 显示这些变量,请使用 ls(all.names=TRUE)。通过在变量的首字母中使用点号,可以改变变量本身的范围。例如:

x <- 3
.x <- 4

ls()
[1] "x"

ls(all.names=TRUE)
[1] ".x" "x" 

x
[1] 3
.x
[1] 4

4. 其他可能的原因

在Hadley的包中,他使用了在函数名前加点号的约定。这是一种机制来尝试确保解析变量名时,值会解析为用户变量而不是内部函数变量。


复杂情况

这些不同用途的混合可能会导致非常混乱的情况,因为这些不同的用途可以混在同一个函数名中。

例如,要将data.frame转换为列表,您使用as.list(..)

as.list(iris)

在这种情况下,as.list是一个S3通用方法,并且您正在将data.frame传递给它。因此,将调用S3函数as.list.data.frame

> as.list.data.frame
function (x, ...) 
{
    x <- unclass(x)
    attr(x, "row.names") <- NULL
    x
}
<environment: namespace:base>

如果想要查看一些真正精彩的内容,可以加载 data.table 包,并查看函数 as.data.table.data.frame

> library(data.table)

> methods(as.data.table)
[1] as.data.table.data.frame* as.data.table.data.table* as.data.table.matrix*    

   Non-visible functions are asterisked


> data.table:::as.data.table.data.frame
function (x, keep.rownames = FALSE) 
{
    if (keep.rownames) 
        return(data.table(rn = rownames(x), x, keep.rownames = FALSE))
    attr(x, "row.names") = .set_row_names(nrow(x))
    class(x) = c("data.table", "data.frame")
    x
}
<environment: namespace:data.table>

1
“有点隐藏”是什么意思?点号不会改变变量的作用域,对吧?所以它只是在用户通常不在变量名开头使用点时才被隐藏,对吗? - Tomas
1
但这并不是通常所说的“作用域”。作用域通常是指全局/局部(对于函数、包等),而不仅仅是隐藏在ls()中(但仍然可以访问)。 - Tomas
2
哦,我明白你的意思了。不,x.x的作用域行为是相同的。它们只是不同的变量。 - Andrie
3
我只在plyr中这样做,因为你需要区分传入plyr函数的参数和plyr所调用的函数的参数。 - hadley
3
plyr还有一个叫做.的函数。get(".")的作用是将输入的参数转换为一个列表形式,并返回一个名为"quoted"的对象,这个对象可以被用来进行进一步处理。其中,...表示可变数量的参数,.env = parent.frame()表示该函数所处的环境。 - baptiste
显示剩余4条评论

31

在名称的开头,它的作用类似于 UNIX 文件名约定,可以默认隐藏对象。

ls()
character(0)

.a <- 1

ls()
character(0)

ls(all.names = TRUE)
[1] ".a"

它可以只是一个没有特殊含义的标记,它不会比其他允许的标记做更多的事情。

my.var <- 1
my_var <- 1
myVar <- 1

这是用于S3方法分派的。所以,如果我定义了一个简单类“myClass”,并创建了具有该类属性的对象,则诸如print()之类的通用函数将自动分派到我的特定打印方法。

myvar <- 1

print(myvar)

class(myvar) <- c("myClass", class(myvar))

print.myClass <- function(x, ...) {

    print(paste("a special message for myClass objects, this one has length", length(x)))
    return(invisible(NULL))
}

print(myvar)

在S3语法中存在歧义,因为从函数名称无法判断它是S3方法还是名称中的点。但是,这是一种非常简单但非常强大的机制。

每个方面都有更多的内容,你不应该将我的示例视为良好的实践,但它们是基本区别。


3
你为什么想要隐藏一个不是全局的对象? - Matt Bannert
3
你也可以将变量或函数定义为单个点!例如 .=1.(1)。这真的很奇怪,特别是在表达式 .*.+. 中 :-) - Tomas

0
如果用户定义了一个函数.doSomething,但懒得为参数指定所有的roxygen文档,那么编译包时不会产生错误。

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