为什么有人使用{}来初始化R中的空对象?

问题描述 投票:12回答:2

似乎有些programmers正在使用:

a = {}
a$foo = 1
a$bar = 2

a = list(foo = 1, bar = 2)有什么好处?

为什么要使用{}?这个表达式只返回NULL,所以NULL赋值也会这样做,不是吗?

r null
2个回答
12
投票

Your first query

为什么要使用{},这个表达式只返回NULL,所以NULL赋值也会这样做,不是吗?

是的,a <- NULL给出了同样的效果。使用{}很可能是个人风格。


NULL

NULL可能是最通用和令人困惑的R对象。来自R language definition of NULL

只要需要指示或指定对象不存在,就会使用它。它不应与矢量或零长度列表混淆。

NULL对象没有类型,也没有可修改的属性。 R中只有一个NULL对象,所有实例都引用该对象。要测试NULL使用is.null。您无法在NULL上设置属性。

严格来说,NULL只是NULL。这是is.null唯一回归TRUE的东西。但是,据?NULL说:

值为NULL的对象可以由替换运算符更改,并将被强制转换为右侧的类型。

因此,虽然它与具有合法模式的长度为0的向量不同(在向量中不允许R中的所有模式;对于完整的模式列表,读取?mode,对于向量的合法,读取?vector),这种灵活性强制通常使其行为类似于长度为0的向量:

## examples of atomic mode
integer(0)  ## vector(mode = "integer", length = 0)
numeric(0)  ## vector(mode = "numeric", length = 0)
character(0)  ## vector(mode = "character", length = 0)
logical(0)  ## vector(mode = "logical", length = 0)

## "list" mode
list()  ## vector(mode = "list", length = 0)

## "expression" mode
expression()  ## vector(mode = "expression", length = 0)

你可以做矢量连接:

c(NULL, 0L)  ## c(integer(0), 0L)
c(NULL, expression(1+2))  ## c(expression(), expression(1+2))
c(NULL, list(foo = 1))  ## c(list(), list(foo = 1))

你可以增长一个向量(正如你在你的问题中所做的那样):

a <- NULL; a[1] <- 1; a[2] <- 2
## a <- numeric(0); a[1] <- 1; a[2] <- 2

a <- NULL; a[1] <- TRUE; a[2] <- FALSE
## a <- logical(0); a[1] <- TRUE; a[2] <- FALSE

a <- NULL; a$foo <- 1; a$bar <- 2
## a <- list(); a$foo <- 1; a$bar <- 2

a <- NULL; a[1] <- expression(1+1); a[2] <- expression(2+2)
## a <- expression(); a[1] <- expression(1+1); a[2] <- expression(2+2)

使用{}生成NULL类似于expression()。虽然不完全相同,但是当你稍后用它做某事时,运行时强制确实使它们难以区分。例如,在生成列表时,以下任何一个都可以工作:

a <- NULL; a$foo <- 1; a$bar <- 2
a <- numeric(0); a$foo <- 1; a$bar <- 2  ## there is a warning
a <- character(0); a$foo <- 1; a$bar <- 2  ## there is a warning
a <- expression(); a$foo <- 1; a$bar <- 2
a <- list(); a$foo <- 1; a$bar <- 2

对于具有原子模式的长度为0的向量,在运行时强制期间会产生警告(因为从“原子”到“递归”的变化太大):

#Warning message:
#In a$foo <- 1 : Coercing LHS to a list

我们没有得到表达式设置的警告,因为来自?expression

作为模式的对象'“表达”'是一个列表......

嗯,它不是通常意义上的“清单”;它是一个类似列表的抽象语法树。


Your second query

a = list(foo = 1, bar = 2)有什么好处?

这样做没有任何好处。您应该已经在其他地方读过,在R中,不断增长的对象是一种不好的做法。谷歌的随机搜索给出了:growing objects and loop memory pre-allocation

如果您知道向量的长度以及每个元素的值,请直接创建它,如a = list(foo = 1, bar = 2)

如果您知道向量的长度,但要计算其元素的值(例如通过循环),请设置向量并执行填充,如a <- vector("list", 2); a[[1]] <- 1; a[[2]] <- 2; names(a) <- c("foo", "bar")


In reply to Tjebo

我实际上查了?mode,但它没有列出可能的模式。它指向?typeof,然后指​​向src/main/util.c中结构TypeTable中列出的可能值。我甚至找不到这个文件夹(OSX)。知道在哪里找到这个吗?

它表示R分发的来源,它是CRAN上的“.tar.gz”文件。另一种选择是查看https://github.com/wch/r-source。无论哪种方式,这是表:

TypeTable[] = {
    { "NULL",       NILSXP     },  /* real types */
    { "symbol",     SYMSXP     },
    { "pairlist",   LISTSXP    },
    { "closure",    CLOSXP     },
    { "environment",    ENVSXP     },
    { "promise",    PROMSXP    },
    { "language",   LANGSXP    },
    { "special",    SPECIALSXP },
    { "builtin",    BUILTINSXP },
    { "char",       CHARSXP    },
    { "logical",    LGLSXP     },
    { "integer",    INTSXP     },
    { "double",     REALSXP    }, /*-  "real", for R <= 0.61.x */
    { "complex",    CPLXSXP    },
    { "character",  STRSXP     },
    { "...",        DOTSXP     },
    { "any",        ANYSXP     },
    { "expression", EXPRSXP    },
    { "list",       VECSXP     },
    { "externalptr",    EXTPTRSXP  },
    { "bytecode",   BCODESXP   },
    { "weakref",    WEAKREFSXP },
    { "raw",        RAWSXP },
    { "S4",     S4SXP },
    /* aliases : */
    { "numeric",    REALSXP    },
    { "name",       SYMSXP     },

    { (char *)NULL, -1     }
};

6
投票

Per R关于大括号和括号的文档(键入?'{'来读取它们),大括号返回其中评估的最后一个表达式。

在这种情况下,a <- {}基本上“返回”一个空对象,因此等同于a <- NULL,它建立一个空变量,然后可以将其视为一个列表。

顺便说一句,这就是为什么可以编写R函数,只需将返回变量的名称作为函数的final语句写入,就可以返回函数的输出。例如:

function(x) {
    y <- x * 2
    return(y)
}

相当于:

function(x) {
    y <- x * 2
    y
}

甚至:

function(x) {
    y <- x * 2
}

作为赋值的函数的最后一行禁止在控制台中打印结果,但如果函数保存到变量中,函数肯定会返回预期值。

© www.soinside.com 2019 - 2024. All rights reserved.