我对 感兴趣?typeof,它提到了可以返回的值。有没有办法打电话typeof(something)并获得以下其中一项?
“承诺”、“字符”、“...”、“任何”、“字节码”
我发现我可以获得两种更奇特的类型,帮助认为typeof“不太可能在用户级别看到”,如下所示:
typeof(new("externalptr"))
# [1] "externalptr"
typeof(rlang::new_weakref(new("externalptr")))
# [1] "weakref"
Run Code Online (Sandbox Code Playgroud)
但有办法得到其他人吗?
All*_*ron 38
在我们尝试从 中获取具体响应之前typeof,让我们先澄清一下该函数的实际用途。这需要回顾一下什么是类型R 中的
类型
\n每个 R 对象都由底层 C 代码中称为 an 的结构表示SEXP,其中包含指向实际数据的指针。由于可以指向不同类型的数据结构,因此每种数据结构都有一个名为 的字段,该字段告诉 R所指向的结构类型。它存储为整数。SEXPSEXPSEXPTYPESEXPSEXPTYPE
当我们调用R 时,会在类型表中查找typeof对象的整数值,最终将字符串返回到控制台,以给出对象的人类可读的描述。因此,类型表包含的所有可能输出。SEXTYPESEXPTYPEtypeof
从这个意义上说, R 中对象的类型是对它是什么类型的对象的最低级别的描述。
\n类型表中条目的值SEXPTYPE如下:
| 价值 | 性别类型 | 描述 | typeof输出 |
|---|---|---|---|
| 0 | NILSXP | 无效的 | “无效的” |
| 1 | SYMSXP | 符号 | “象征” |
| 2 | 列表XP | 配对表 | “配对列表” |
| 3 | 闭环XP | 关闭 | “关闭” |
| 4 | 环境变量XP | 环境 | “环境” |
| 5 | PROMSXP | 承诺 | “承诺” |
| 6 | 朗斯XP | 语言对象 | “语言” |
| 7 | 特别SXP | 特殊功能 | “特别的” |
| 8 | 内置XP | 内置函数 | “内置” |
| 9 | CHARSXP | 内部字符串 | “字符” |
| 10 | LGLSXP | 逻辑向量 | “逻辑” |
| 13 | INTSXP | 整数向量 | “整数” |
| 14 | RELSXP | 数值向量 | “双倍的” |
| 15 | CPLXXXP | 复杂向量 | “复杂的” |
| 16 | STRSXP | 字符向量 | “特点” |
| 17 号 | DOTSXP | 点-点-点对象 | “……” |
| 18 | 任何SXP | 使 \xe2\x80\x9cany\xe2\x80\x9d 参数工作 | “任何” |
| 19 | VECSXP | 列表(通用向量) | “列表” |
| 20 | EXPRSXP | 表达载体 | “表达” |
| 21 | BCODEXXP | 字节码 | “字节码” |
| 22 | EXPTRSXP | 外部指针 | “外部指针” |
| 23 | 弱引用XP | 弱引用 | “弱引用” |
| 24 | RAWSXP | 原始向量 | “生的” |
| 25 | S4SXP | S4 类不是简单类型 | “S4” |
可以在控制台中获取每种类型的对象,但据我所知,其中四种无法单独在基础 R 中获取。它们是“promise”、“char”、“any”和“weakref”。对于这些,我们需要使用额外的编译代码 - 要么是我们自己的小片段Rcpp,要么是 中已经可用的函数rlang。
让我们在控制台中获取每个有效类型的示例。
\n0:NILSXP
\n这只是NULL
n <- NULL\ntypeof(n)\n#> [1] "NULL"\nRun Code Online (Sandbox Code Playgroud)\n1:SYMSXP
\n这是一个未评价的symbol。我们可以通过多种方式在基数 R 中获得符号,包括quote、substitute和bquotestr2lang
s <- quote(x)\ntypeof(s)\n#> [1] "symbol"\nRun Code Online (Sandbox Code Playgroud)\n2:列表XP
\n尽管有这个名称,但这并不用于对象list,而是用于点对列表,如formalsof 函数中使用的那样。从功能上讲,它们与标准列表类似,但在底层 C 代码中实现方式不同,并且确实有一些重要的区别
p <- pairlist(a = 1)\ntypeof(p)\n#> [1] "pairlist"\nRun Code Online (Sandbox Code Playgroud)\n3:关闭XP
\n它用于存储闭包,即用 R 代码编写的函数,而不是内部 C 函数。
\nf <- function() {}\ntypeof(f)\n#> [1] "closure"\nRun Code Online (Sandbox Code Playgroud)\n4:环境XP
\n用于存储环境
\ne <- new.env()\ntypeof(e)\n#> [1] "environment"\nRun Code Online (Sandbox Code Playgroud)\n5:PROMSXP
\n在 R 中,promise由两个对象组成:一块未评估的代码,加上一个指向应在其中评估该代码的环境的指针。这与quosuretidyverse 生态系统中的 a 非常相似,除了可以很容易地分配和传递 quosure,延迟评估直到需要时为止。承诺更加转瞬即逝;一旦您将其分配给一个符号,它就会立即进行评估,因此要在野外查看一个符号,您需要将其包含在列表中。
我找不到一种仅使用基础 R 创建一个的方法,但我们可以使用rlang::node_cara获得承诺DOTSXP。
p <- list(rlang::node_car((function(...) get("..."))(a = 1)))\n\np\n#> [[1]]\n#> <promise: 0x0000021e05c3f870>\n\ntypeof(p[[1]])\n#> [1] "promise"\nRun Code Online (Sandbox Code Playgroud)\n6:LANGSXP
\n这只是一段未评估的代码(尽管在存储之前已将其解析为语法正确)。同样,这可以通过quote或创建substitute,但公式也存储为语言对象:
l <- hello ~ world\ntypeof(l)\n#> [1] "language"\nRun Code Online (Sandbox Code Playgroud)\n7:特殊XP
\n这仅用于将未计算的参数传递给内部 R 机制的原始函数:
\ni <- `if`\ntypeof(i)\n#> [1] "special"\nRun Code Online (Sandbox Code Playgroud)\n8:内置XP
\n同样,这仅用于存储内置函数,但它们与“特殊”函数不同,因为它们的参数在传递给内部代码之前在 R 中进行计算。
\nb <- `+`\ntypeof(b)\n#> [1] "builtin"\nRun Code Online (Sandbox Code Playgroud)\n9: 字符XP
\n它们不用于存储 R\ 熟悉的字符向量,而是 R 内部用于存储原子字符串的字符类型。这允许缓存可重用的字符串,并允许字符向量(类型STRSXP)更加高效。请注意,R 不喜欢处理CHARSXP其内部函数之外的内容。当你在控制台中有一个时,它会发出警告,告诉你这种类型的对象不能有属性。
与直觉相反,这是最难制作的之一。也许最简单的方法是创建一个RAWSXP然后更改编译代码中的基础类型。
Rcpp::cppFunction("SEXP mkchar(SEXP s) {SET_TYPEOF(s, 9); return s;}")\n\nget_char <- function(){\n mkchar(as.raw(c(0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, \n 0x6c, 0x64, 0x21, 0x00)))\n}\n\nchr <- get_char()\n\nchr\n#> <CHARSXP: "Hello World!">\n\ntypeof(chr)\n#> [1] "char"\nRun Code Online (Sandbox Code Playgroud)\n10:LGLSXP
\n这些是R中常用的逻辑向量。
\nl <- TRUE\ntypeof(l)\n#> [1] "logical"\nRun Code Online (Sandbox Code Playgroud)\n如果您想知道 SEXPTYPE 11 和 12,它们以前在几十年前用于因子和有序因子,但甚至不再定义,因此我们没有可以从中提取与typeof这些相对应的隐藏遗留类型隐藏遗留类型。
13:INTSXP
\nR对整数和双精度浮点数使用不同的类型,但只要有一点点的刺激就会从整数转换为双精度数。整数和双精度数之间的差异在某种程度上从最终用户那里抽象出来,因为INTSXP和REALSXP被集中在一起作为众数 "numeric"
I <- 1L\ntypeof(I)\n#> [1] "integer"\nRun Code Online (Sandbox Code Playgroud)\n14:REALSXP
\n这些是熟悉的数值向量
\nr <- 1.1\ntypeof(r)\n#> [1] "double"\nRun Code Online (Sandbox Code Playgroud)\n15:CPLXSXP
\n复数有自己的存储类型并且很容易创建。我不确定为什么他们需要自己的存储类型,因为它们似乎可以在 S3 中轻松实现。据推测,这部分是历史原因,部分是由于与各种数学库接口的效率。
\nC <- 1 + 1i\ntypeof(C)\n#> [1] "complex"\nRun Code Online (Sandbox Code Playgroud)\n16:STRSXP
\n这是熟悉的字符向量。
\ns <- "Hello world"\ntypeof(s)\n#> [1] "character"\nRun Code Online (Sandbox Code Playgroud)\n17:DOTSXP
\n某些函数形式中允许传递额外的任意参数的点被实现为承诺对列表。它有自己的存储模式,称为 DOTSXP
\n也许令人惊讶的是,这实际上可以在没有任何编译代码的情况下获得:
\nd <- (function(...) get("..."))(a = 1)\n\nd\n#> <...>\n\ntypeof(d)\n#> [1] "..."\nRun Code Online (Sandbox Code Playgroud)\n18:任何SXP
\n这并不是一个真正定义明确的存储模式。据我所知,它在内部用作替代品,主要用于 S4 对象的实现。如果您尝试显示“any”类型的对象,控制台将给出错误,但它可以被存储并且其类型可以正确报告。我看不到一种方法来获取它,而无需通过编译代码强制现有对象:
\nRcpp::cppFunction("SEXP get_any(SEXP s) {SET_TYPEOF(s, 18); return s;}")\n\na <- get_any(1:5)\n\na\n#> Error: unimplemented type \'any\' in \'PrintValueRec\'\n\ntypeof(a)\n#> [1] "any"\nRun Code Online (Sandbox Code Playgroud)\n19:VECSXP
\n这就是大家熟悉的万能Rlist
l <- list()\ntypeof(l)\n#> [1] "list"\nRun Code Online (Sandbox Code Playgroud)\n20:EXPRSXP
\n用于未计算的表达式(列表)
\ne <- expression(hello * world)\ntypeof(e)\n#> [1] "expression"\nRun Code Online (Sandbox Code Playgroud)\n21:BCODEXXP
\n用于编译函数的字节代码。
\nb <- .Internal(bodyCode(mean))\ntypeof(b)\n#> [1] "bytecode"\nRun Code Online (Sandbox Code Playgroud)\n22:EXTPTRSXP
\n这已经在问题中提到过,在这里是为了完整性
\ne <- new("externalptr")\ntypeof(e)\n# [1] "externalptr"\nRun Code Online (Sandbox Code Playgroud)\n23:弱引用XP
\n这已经在问题中提到过,在这里是为了完整性
\nw <- rlang::new_weakref(.GlobalEnv)\ntypeof(w)\n#> [1] "weakref"\nRun Code Online (Sandbox Code Playgroud)\n24:RAWXP
\n这只是一个无符号 8 位整数数组
\nr <- as.raw(1L)\ntypeof(r)\n#> [1] "raw"\nRun Code Online (Sandbox Code Playgroud)\n25:S4SXP
\n这用于在本机面向对象的 S4 系统中创建的对象
\nsetClass("R_obj", slots = c(a = "character", b = "numeric"))\ns <- new("R_obj", a = "Hello world", b = 1)\ntypeof(s)\n#> [1] "S4"\nRun Code Online (Sandbox Code Playgroud)\n除了这 24 种类型之外,还定义了另外 3 种 SEXPTYPE,它们在类型查找表中没有名称,因此无法从typeof. 它们是 30 (NEWSXP)、31 (FREESXP) 和 99 (FUNSXP)。前两个在内部用于内存管理/垃圾收集,并且应该只存在微秒,第三个用作占位符 SEXPTYPE,用于在搜索模式函数的对象时将闭包/内置函数/特殊函数集中在一起。据我所知,没有任何 SEXP 实际上具有这种 SEXTYPE。
我很想知道是否有人有办法在不使用 rlang / Rcpp 的情况下创建 PROMSXP 或 WEAKREFSXP。如果能听到有关在不使用编译代码的情况下创建 CHARSXP 或 ANYSXP 的任何方法(尽管这些在控制台中使用时似乎有点不稳定,但它们是生成的),那也是件好事。
\n最后一点,在讨论类型时会出现密切相关的模式、存储模式和类概念。mode和storage.mode本质上都是类型的别名,如下所述:
\n存储方式
\n调用storage.mode(x)只是调用typeof(x)并返回它,除非typeof(x)是“closure”、“builtin”或“special”,然后storage.mode返回“function”。因此,它只是类型的轻微抽象/简化。
模式
\n该调用mode(x)还将闭包/特殊/内置函数调用typeof(x)并简化为单一模式“函数”。此外,它对于整数和实数类型都返回“numeric”。它将“符号”更改为“名称”,并将“语言”更改为“调用”或“(”,具体取决于语言对象是否以括号开头。
班级
\n你可以每天使用 R,而不需要了解任何有关类型、模式和存储模式的信息,但是一个称职的 R 用户需要了解类的概念。它是一个对象的类,它确定在调用泛型函数时分派哪些方法,因此它是控制对象行为的类。
\n您可以通过设置对象的类属性来设置对象的类:
\nx <- 1\nclass(x) <- "foo"\nclass(x)\n#> [1] "foo"\nRun Code Online (Sandbox Code Playgroud)\n然而, R 中的每个对象都有一个类,即使是没有类属性的对象:
\nx <- 1:5\nclass(x)\n#> [1] integer\nRun Code Online (Sandbox Code Playgroud)\n这是因为 R 通过 C 函数do_data_class确定对象的类。如果有一个“class”属性集,那么那就是类。如果没有设置“类”属性,则首先 R 将检查维度属性。如果存在非零维度属性,则该类将是一个“数组”(尽管如果它恰好具有二维,则它将具有 class c("matrix", "array"))。如果没有类或维度属性,则检索对象的类型。根据类型,R 将返回:
"function"用于内置或特殊的封闭件"numeric"为REALSXP(尽管令人惊讶地不是为INTSXP)"name"对于符号if、while、for、=、<-或(,{则将返回该符号。否则"call"返回。这是一个相当神秘的系统,它似乎是处理语言语法不同元素的一种方式。typeof对象。总之,类型是存储在内存中的对象的实际类型,而模式是实际类型的部分抽象,它为我们提供了我们通常认为是 R 中“基本类型”的熟悉名称。存储模式是用途有限的类型。类是 R 中最熟悉、最有用的数据类型抽象,如果一个对象没有指定的类,R 将为它分配一个隐式类根据上述规则
\nPromise 最常在调用函数时创建:您指定的参数成为 Promise。然而,很难(不可能?)返回typeof“promise”,因为 R 代码会导致promise 被求值,并且它会变成不同的类型。您可以将其视为 C 代码中的承诺,但在 R 中则不然。
“char”是字符向量中单个条目的类型。你永远无法在 R 代码中看到这一点。
“...”是参数中传递的函数的参数列表...。这有点像一个承诺;当你看着它时,你通常会强迫它变成别的东西。然而,@moodymudskipper 指出environment()$...返回实际...对象。
“any”在 S4 系统中以某种方式使用;我不知道你是否可以创建一个赤裸裸的例子。
“字节码”是函数的编译版本。一旦函数被编译(包中的大多数函数都将被编译),您可以使用获取其字节码.Internal(bodyCode(fn)),例如
x <- .Internal(bodyCode(mean))
typeof(x)
#> [1] "bytecode"
compiler::disassemble(x)
#> list(.Code, list(11L, CALLSPECIAL.OP, 0L, RETURN.OP), list(UseMethod("mean"),
#> structure(c(NA, 0L, 0L, 0L), class = "expressionsIndex")))
Run Code Online (Sandbox Code Playgroud)