内置获取列表元素或默认?

时间:2014-10-25 10:43:46

标签: r

R中是否有内置函数或(流行)包返回特定的列表元素或默认值?

示例:

> l <- list(a=1, b=2, c=3)
> getElementOrDefault(l, 'b', 0)
[1] 2
> getElementOrDefault(l, 'd', 0)
[1] 0

我正在寻找getElementOrDefault的实际名称。 我知道从头开始编写这样的函数并不难,但使用内置函数有其优点。所以请不要写getElementOrDefault的代码来回答。

1 个答案:

答案 0 :(得分:5)

  

tl; dr: 没有内置的解决方案,但R更自然地为扩展其访问者语法而不是替换它。如果你不耐烦,请跳到水平分隔符之后。

  

使用内置有其优势

这是一个公平的观点(正如你在评论中所解释的那样),但由于似乎没有默认值,因此这一点是没有实际意义的。但是,我们可以稍微小心地定义这样的操作,使其行为与内置操作的行为方式相同。

最简单的说,我们只需将索引转发给[[运算符 - 内置 - 从而避免因必须处理不同类型的索引而引起的所有复杂情况:< / p>

item_or_default = function (list, item, default = NULL) {
    value = list[[item]]
    if (is.null(value)) default else value
}

这里唯一需要注意的是,通过[[建立索引通常是递归的,但在这里我们只支持单个索引。

这种方法无法区分缺失值和NULL,因为内置索引操作也没有区分。这可以通过检查索引是否超出边界(如果它是数字)来修复,或者如果索引是字符串则是否缺失。该实现留给读者练习。

作为替代方案,我们可以覆盖现有的[[运算符,并通过default参数对其进行扩充:

`[[` = function (x, ...) {
    `[[` = previous_definition('[[')
    if (class(x) != 'list')
        return(x[[...]])

    # NB: length of index can be > 1, to allow for recursive indexing.
    args = list(...)
    default = args$default
    args$default = NULL
    value = do.call(`[[`, c(list(x), args))
    if (is.null(value)) default else value
}

previous_definition = function (name) {
    # Skip the parent environment of the calling frame.
    get(name, envir = parent.env(parent.env(parent.frame())))
}

这大部分都是直截了当的。请注意,我们必须根据x的类型手动调度操作符,因为覆盖[[.list不起作用(即使文档声称运算符是通用的)。 previous_definition为我们提供了[[的先前定义,忽略了重新定义。因此,我们避免递归地调用相同的函数。

有意定义函数,使default参数必须命名为,以允许递归索引,并避免混淆。举个例子:

test = list(a = 1)
test[['a', default = 42]]
# 1
test[['b', default = 42]]
# 42
test[['a', 42]]
# Error …: Incorrect number of subscripts

但最后,所有这些解决方案都有点奇怪。我不会在实际代码中使用它们中的任何一个。相反,我使用以下内容:

`%or%` = function(a, b) {
    cmp = function(a,b) if (identical(a, FALSE) ||
                            is.null(a) ||
                            is.na(a) ||
                            is.nan(a) ||
                            length(a) == 0) b else a

    if (length(a) > 1)
        mapply(cmp, a, b)
    else
        cmp(a, b)
}

这允许我编写以下代码(以及更多):

test$a %or% 42
# 1
test$b %or% 42
# 42