什么是对象?

3

考虑以下荒谬的输入和输出:

> names(function(x) x*x)<-"square"
Error in names(function(x) x * x) <- "square" : 
  target of assignment expands to non-language object

隐含地,这个错误表明function(x) x * x是一个对象,但不是语言对象。然而,names()的文档表示它允许"一个 R 对象"作为其输入,与此前提相矛盾。此外,语言定义中指出"R 函数是对象",这更加令人困惑。最后,is.object(function(x) x * x) 返回FALSE,但我认为这是由于类别原因导致的。
我的问题是:什么是对象,为什么names()会将function(x) x * x视为非对象?

虽然不是回答问题的答案,但对于那些需要的人,如果有的话,这个可以工作:assign(x = "square", function(x) x*x) - MrSmithGoesToWashington
@MrSmithGoesToWashington 我认为这不是同一件事。问题中的代码试图将名称“square”分配给函数。我相信你所做的是将函数分配给变量名“square”。我相信你的代码等价于assign("square", function(x) x*x)square<-function(x) x*x - J. Mini
哦,是的,你说得完全正确。在你的情况下,你是否打算让你的代码导致square(24)成为可接受的代码呢? - MrSmithGoesToWashington
@MrSmithGoesToWashington 不是的。我正在尝试在匿名函数上使用 names 函数。 - J. Mini
3个回答

6
你遇到的错误不是由于 function 对象的性质所致,而是因为 names<- 的参数是一个“匿名”对象,而不是连接到符号的对象。考虑以下例子:
names(1:10)<-letters[1:10]
#Error in names(1:10) <- letters[1:10] : 
#  target of assignment expands to non-language object 

相同的输出。在错误中,非语言看起来像是一个非常复杂的方式来表示“不是一个符号”。然而,这很合乎逻辑,因为你调用了一个替换函数,并且你要求替换其内容的符号不存在。根源在于 <- 运算符,它期望赋值操作的左侧是一个符号(一种语言对象),而不是一个“标准”对象:

1:10<-setNames(1:10,letters[1:10])
#Error in 1:10 <- setNames(1:10, letters[1:10]) : 
#  target of assignment expands to non-language object

再次出现了相同的错误。

当然,这个是可行的:

a<-1:10
names(a)<-letters[1:10]

开始进入您的示例:

f<-function(x) x*x
names(f)<-"square"
# Error in names(f) <- "square" : names() applied to a non-vector

你调用“匿名”函数并明确使用names<-时,会出现上述错误:
"names<-"(function(x) x*x, "square")
# Error: names() applied to a non-vector

现在的错误是因为names<-虽然是一个通用函数,但没有针对函数的方法。这在?names的详细信息部分有记录:
“names”是一个通用访问函数,“names<-”是一个通用替换函数。默认方法获取并设置向量(包括列表)或参数列表的“"names"”属性。
关于主要问题“什么是对象”,我只添加了一些内容。在R中,一切都是对象。 is.object函数的名称具有误导性,更好的名称应该是is.internalobjectbitset。文档非常清楚。标题说:“对象是否被内部分类?”而不是像“变量是否是对象?”;在参数部分,对于x,它说:x:要测试的对象,如果您想首先测试x是否为对象,则这相当奇怪。它还指定主要用于内部使用,而不是作为定义什么是对象的方式(这将是一个愚蠢的功能,因为在R中一切都是对象)。
根据评论,我想补充的是,总的来说,文档遗漏了一点,即虽然您可以将names应用于任何R对象,但您无法将names赋值给某些类型。在我看来,这是一个非常小的问题,因为names不仅是普通属性,而且与语言的某些属性(具体来说是子集)密切相关。只有可以进行子集的对象才能拥有名称。函数对象不应被子集化,因此R会阻止分配名称属性,这基本上没有用处。

您的回答强烈表明了 names 的声明是错误的,并非期望 x 是 "an R object"。但是,您也说明 names 是一个通用函数。这是否意味着 "an R object" 在 is.object 的意义上等同于"object" ?文档是不是试图告诉我们 names 理论上可以在任何这样的对象上工作?我不确定 names 的文档是错误的,还是 "an R object" 定义得不够清晰,或者文档试图讨论 is.object 对象。不幸的是,setNames(1:5,letters[1:5]) 不是一个 is.object 对象,但显然有名称。 - J. Mini
不确定您的评论意思。names 实际上接受任何 R 对象。当用户尝试将名称设置为某些类型(如函数)时,替换会出错,因为这些类型不打算具有名称(而且实际上一个函数为什么需要名称属性,当它可以有任意的其他属性)。is.object 函数,尽管其名称如此,但在此处没有作用,并且与 R 中的对象无关。 - nicola
当某些类型的对象会抛出错误时,是否真的算作接受任何 R 对象?我的问题关键在于 names 的文档说 x 可以是“_an R object_”,而你的答案已经展示了 names 无法工作的对象。我不清楚 names 文档是错误的还是“_an R object_”定义不清楚。你的评论是区分 names"names"<- 吗?它们的文档使用相同的 x 参数,所以这里不应该有任何区别吧? - J. Mini
我明白你的意思。事实上,“names”可以接受任何内容,而替换则不行。严格来说,我们可以说文档是错误的,但由于名称用于索引,而索引函数并没有太多意义,所以我认为这只是一个非常小的问题。 - nicola
从上面的内容扩展,还要考虑到名称严格与子集/索引相关联,这是语言本身的属性。在基本的R类型中,您可以为可以进行子集操作的类型分配名称。 - nicola

5

查看R源代码(names.c

{"is.null",       do_is,        NILSXP, 1,  1,  {PP_FUNCALL, PREC_FN,   0}},
{"is.logical",    do_is,        LGLSXP, 1,  1,  {PP_FUNCALL, PREC_FN,   0}},
{"is.integer",    do_is,        INTSXP, 1,  1,  {PP_FUNCALL, PREC_FN,   0}},
{"is.double",     do_is,        REALSXP,1,  1,  {PP_FUNCALL, PREC_FN,   0}},
{"is.complex",    do_is,        CPLXSXP,1,  1,  {PP_FUNCALL, PREC_FN,   0}},
{"is.character",  do_is,        STRSXP, 1,  1,  {PP_FUNCALL, PREC_FN,   0}},
{"is.symbol",     do_is,        SYMSXP, 1,  1,  {PP_FUNCALL, PREC_FN,   0}},
{"is.name",       do_is,        SYMSXP, 1,  1,  {PP_FUNCALL, PREC_FN,   0}},
{"is.environment",do_is,        ENVSXP, 1,  1,  {PP_FUNCALL, PREC_FN,   0}},
{"is.list",       do_is,        VECSXP, 1,  1,  {PP_FUNCALL, PREC_FN,   0}},
{"is.pairlist",   do_is,        LISTSXP,1,  1,  {PP_FUNCALL, PREC_FN,   0}},
{"is.expression", do_is,        EXPRSXP,1,  1,  {PP_FUNCALL, PREC_FN,   0}},
{"is.raw",        do_is,        RAWSXP, 1,  1,  {PP_FUNCALL, PREC_FN,   0}},

{"is.object",     do_is,        50,     1,  1,  {PP_FUNCALL, PREC_FN,   0}},

{"isS4",          do_is,        51,     1,  1,  {PP_FUNCALL, PREC_FN,   0}},
{"is.numeric",    do_is,        100,    1,  1,  {PP_FUNCALL, PREC_FN,   0}},
{"is.matrix",     do_is,        101,    1,  1,  {PP_FUNCALL, PREC_FN,   0}},
{"is.array",      do_is,        102,    1,  1,  {PP_FUNCALL, PREC_FN,   0}},
{"is.atomic",     do_is,        200,    1,  1,  {PP_FUNCALL, PREC_FN,   0}},
{"is.recursive",  do_is,        201,    1,  1,  {PP_FUNCALL, PREC_FN,   0}},
{"is.call",       do_is,        300,    1,  1,  {PP_FUNCALL, PREC_FN,   0}},
{"is.language",   do_is,        301,    1,  1,  {PP_FUNCALL, PREC_FN,   0}},

{"is.function",   do_is,        302,    1,  1,  {PP_FUNCALL, PREC_FN,   0}},
...

显示objects(50)是一种特定类型,不同于function(302)或其他类型(如numeric, matrix等)。

这在coerce.c中的do_is定义中得到确认:

    case 50:        /* is.object */
    LOGICAL0(ans)[0] = OBJECT(CAR(args));
    break;

对象具有以下方法:

/* Objects */
{"inherits",    do_inherits,    0,  11, 3,  {PP_FUNCALL, PREC_FN,   0}},
{"UseMethod",   do_usemethod,   0,     200, -1, {PP_FUNCALL, PREC_FN,   0}},
{"NextMethod",  do_nextmethod,  0,     210, -1, {PP_FUNCALL, PREC_FN,   0}},
{"standardGeneric",do_standardGeneric,0, 201,   -1, {PP_FUNCALL, PREC_FN,   0}},

指向R基础文档的点:

R拥有一种简单的通用函数机制,可用于面向对象式编程。方法分发是基于通用函数的第一个参数或作为UseMethod或NextMethod参数提供的对象的类进行的。

这表明类型为50的对象(通过is.object检测)只是S3类或S4类,并在is.object文档中得到确认:

is.object - 对象是否被内部分类?如果对象x具有R内部OBJECT位,则返回TRUE,否则返回FALSE。当添加“class”属性时,将设置OBJECT位,而当删除该属性时,将删除该位,因此这是一种非常有效的检查对象是否具有类属性的方法。(S4对象总是应该有。)

简而言之:内部对象已设置class属性

与普通语言中的对象相比,这相当受限制。不确定R文档在谈论对象时是否总是指objects(50)

关于names(function())错误,请参见@nicola的澄清。


关于名称(function()),错误将此对象视为非语言对象,这可能被理解为不是对象(50)。- 你怎么知道的? - J. Mini
1
我不确定,因此使用了条件形式。 - Waldi
1
你说的是正确的,除了错误的解释不取决于输入是否为函数。请看我的回答。 - nicola
经进一步阅读,我认为这个答案的一些内容是误导性的。例如,“objects(50) are a specific type”: 这是不正确的。你已经像每个选项互斥一样呈现了那个列表。例如,一个对象可以同时通过is.logicalis.object。标记50不是R对象的固有值;相反,它只是do_is函数的参数,告诉函数要执行哪种类型的测试。 - nicola

0

语法错误和措辞不当的错误信息很少能说明语言中对象的状态。这是一个误导。


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