什么是" {"在R?

时间:2015-05-31 19:52:55

标签: r expression

以下是代码:

mf = function(..., expr) {
    expr = substitute(expr)
    print(class(expr))
    print(str(expr))
    expr
}
mf(a = 1, b = 2, expr = {matrix(NA, 4, 4)})

输出:

[1] "{"
length 2 {  matrix(NA, 4, 4) }
 - attr(*, "srcref")=List of 2
  ..$ :Class 'srcref'  atomic [1:8] 1 25 1 25 25 25 1 1
  .. .. ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment: 0x7fbcdbce3860> 
  ..$ :Class 'srcref'  atomic [1:8] 1 26 1 41 26 41 1 1
  .. .. ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment: 0x7fbcdbce3860> 
 - attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment: 0x7fbcdbce3860> 
 - attr(*, "wholeSrcref")=Class 'srcref'  atomic [1:8] 1 0 1 42 0 42 1 1
  .. ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment: 0x7fbcdbce3860> 
NULL
{
matrix(NA, 4, 4)
}

显然substitute(expr)的结果产生了一些类&#34; {&#34;。这门课究竟是什么?为什么长度为2的{matrix(NA, 4, 4)}?这些奇怪的影响是什么意思?

2 个答案:

答案 0 :(得分:10)

{是代码块的类。只要查看课程,请注意这些

之间的区别
mf(a = 1, b = 2, expr = {matrix(NA, 4, 4)})
# [1] "{"
mf(a = 1, b = 2, expr = matrix(NA, 4, 4))
# [1] "call"

{类可以包含多个语句。 length()表示块中有多少语句(包括块的开头)。例如

length(quote({matrix(NA, 4, 4)}))
# [1] 2
length(quote({matrix(NA, 4, 4); matrix(NA,3,3)}))
# [1] 3
length(quote({}))
# [1] 1

属性&#34; srcref&#34;和&#34; srcfile&#34; R轨道是如何定义函数来尝试提供信息性错误消息的。您可以查看?srcfile帮助页面以获取更多相关信息。

答案 1 :(得分:5)

你所看到的是R通过自己的数据结构暴露其内部语言结构的方式的反映。

substitute()函数返回R表达式的解析树。解析树是语言元素的树。这些可以包括文字值,符号(基本上是变量名),函数调用和支撑块。以下是substitute()返回的所有R语言元素的演示,显示了所有R类型分类方案中的类型:

tmc <- function(x) c(typeof(x),mode(x),class(x));
tmc(substitute(TRUE));
## [1] "logical" "logical" "logical"
tmc(substitute(4e5L));
## [1] "integer" "numeric" "integer"
tmc(substitute(4e5));
## [1] "double"  "numeric" "numeric"
tmc(substitute(4e5i));
## [1] "complex" "complex" "complex"
tmc(substitute('a'));
## [1] "character" "character" "character"
tmc(substitute(somevar));
## [1] "symbol" "name"   "name"
tmc(substitute(T));
## [1] "symbol" "name"   "name"
tmc(substitute(sum(somevar)));
## [1] "language" "call"     "call"
tmc(substitute(somevec[1]));
## [1] "language" "call"     "call"
tmc(substitute(somelist[[1]]));
## [1] "language" "call"     "call"
tmc(substitute(somelist$x));
## [1] "language" "call"     "call"
tmc(substitute({blah}));
## [1] "language" "call"     "{"

注意:

  • 请注意所有三种类型的分类方案都非常相似,但略有不同。这可能是混乱的根源。 typeof()给出了对象的存储类型,有时称为“内部”类型(说实话,它可能不应该被称为“内部”,因为 经常非常直接暴露给它R级别的用户,但通常用这种方式描述;我称之为“基础”或“基础”类型),mode()给出了一个类似的分类方案,每个人都应该忽略,{{1给出隐式(如果没有class()属性)或显式(如果有)对象的类,用于S3方法查找(并且应该说,有时直接由R代码检查) ,独立于S3查找过程。)
  • 请注意class是一个逻辑文字,但TRUE是一个符号,就像任何其他变量名一样,并且恰好在默认情况下恰好分配给T(并且同上TRUEF)。这就是为什么有时人们建议不要使用FALSET来支持使用FTRUE,因为FALSET可以重新分配(但我个人更喜欢使用FT来表示简洁性;没有人应该重新分配那些!)。
  • 精明的读者会注意到,在我的文字演示中,我省略了原始类型。这是因为在R中没有原始文字这样的东西。实际上,很少有方法可以在R中保持原始向量; Fraw()as.raw()charToRaw()是我所知道的唯一方式,如果我在rawConnectionValue()电话中使用了这些功能,它们将作为substitute()对象返回,就像在"call"示例中一样,而不是文字原始值。对于列表类型也可以这样说;没有列表文字这样的东西(虽然有很多方法可以通过函数调用获取列表)。对于所有三种类型分类,普通原始向量返回sum(somevar),对于所有三种类型分类,普通列表返回'raw'

现在,当您的解析树比简单的文字值或符号(意味着它必须是函数调用或支撑表达式)更复杂时,您通常可以通过强制转换为列表来检查该解析树的内容。这就是R通过自己的数据结构公开其内部语言结构的方式。

潜入你的榜样:

'list'

这清楚地说明了为什么pt <- as.list(substitute({matrix(NA,4,4)})); pt; ## [[1]] ## `{` ## ## [[2]] ## matrix(NA, 4, 4) 返回2:这是表示解析树的列表的长度。通常,表达式的支撑被转换为第一个列表组件,其余列表组件是从大括号内的分号分隔语句构建的:

length()

请注意,这与函数调用的工作方式相同,不同之处在于,对于函数调用,列表组件是由函数调用的逗号分隔参数组成的:

as.list(substitute({}));
## [[1]]
## `{`
##
as.list(substitute({a}));
## [[1]]
## `{`
##
## [[2]]
## a
##
as.list(substitute({a;b}));
## [[1]]
## `{`
##
## [[2]]
## a
##
## [[3]]
## b
##
as.list(substitute({a;b;c}));
## [[1]]
## `{`
##
## [[2]]
## a
##
## [[3]]
## b
##
## [[4]]
## c

从上面可以清楚地看出,对于支撑表达式和函数调用,第一个列表组件实际上是表示函数名称的符号。换句话说,open brace 一个函数调用,它只返回它的最后一个参数。就像方括号是普通函数调用一样,在它们之上构建了一个方便的语法,开括号是一个普通的函数调用,在它之上构建了一个方便的语法:

as.list(substitute(sum()));
## [[1]]
## sum
##
as.list(substitute(sum(1)));
## [[1]]
## sum
##
## [[2]]
## [1] 1
##
as.list(substitute(sum(1,3)));
## [[1]]
## sum
##
## [[2]]
## [1] 1
##
## [[3]]
## [1] 3
##
as.list(substitute(sum(1,3,5)));
## [[1]]
## sum
##
## [[2]]
## [1] 1
##
## [[3]]
## [1] 3
##
## [[4]]
## [1] 5

回到您的示例,我们可以通过遍历表示解析树的列表结构来完全探索解析树。我刚写了一个很好的小递归函数,可以很容易地做到这一点:

a <- 4:6;
a[2];
## [1] 5
`[`(a,2);
## [1] 5
{1;2};
## [1] 2
`{`(1,2);
## [1] 2

如您所见,支撑表达式变为函数unwrap <- function(x) if (typeof(x) == 'language') lapply(as.list(x),unwrap) else x; unwrap(substitute(3)); ## [1] 3 unwrap(substitute(a)); ## a unwrap(substitute(a+3)); ## [[1]] ## `+` ## ## [[2]] ## a ## ## [[3]] ## [1] 3 ## unwrap(substitute({matrix(NA,4,4)})); ## [[1]] ## `{` ## ## [[2]] ## [[2]][[1]] ## matrix ## ## [[2]][[2]] ## [1] NA ## ## [[2]][[3]] ## [1] 4 ## ## [[2]][[4]] ## [1] 4 的正常函数调用,接受一个参数,即您编码到其中的单个语句。该语句由对`{`()的单个函数调用组成,带有三个参数,每个参数都是字面值:matrix()NA4。这就是整个解析树。

现在我们可以深入理解4类的含义:它表示一个解析树的元素,它是对"{"函数的函数调用。它恰好与其他函数调用(`{`()而不是"{")不同,但据我所知,这在任何地方都没有意义。另请注意"call"和其他typeof()mode()在表示函数调用的所有解析树元素之间是相同的("language""call")一样的。