以下是代码:
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)}
?这些奇怪的影响是什么意思?
答案 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
(并且同上TRUE
和F
)。这就是为什么有时人们建议不要使用FALSE
和T
来支持使用F
和TRUE
,因为FALSE
和T
可以重新分配(但我个人更喜欢使用F
和T
来表示简洁性;没有人应该重新分配那些!)。F
,raw()
,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()
,NA
和4
。这就是整个解析树。
现在我们可以深入理解4
类的含义:它表示一个解析树的元素,它是对"{"
函数的函数调用。它恰好与其他函数调用(`{`()
而不是"{"
)不同,但据我所知,这在任何地方都没有意义。另请注意"call"
和其他typeof()
和mode()
在表示函数调用的所有解析树元素之间是相同的("language"
和"call"
)一样的。