为R中的命名向量重载$

MSR*_*MSR 5 r

使用命名向量时

vec <- c(a = 1, b = 2)
Run Code Online (Sandbox Code Playgroud)

我经常发现自己vec$a在应该写的时候通过写错误vec["a"]vec[["a"]]使用相应的名称(不带名称)访问相应的值。

我认为vec$a出现错误是违反直觉的,因为$通常会提取命名的事物。这种感觉甚至似乎得到了支持,例如在?Extract示例用法为的中x$name,这是否不完美地适合命名向量?

那让我开始思考是否可能使$运算符超负荷以处理命名向量。但是,我对R中的运算符重载不是很有经验,并且我了解(例如,从这里的答案)在重载基本运算符时应谨慎。

我的互连问题:有,为什么我不应该超载的原因$描述说我不理解?也就是说,在某种程度上,这不是R中的默认值吗?如果没有,我将如何明智地这样做

我了解实际上,即使只是出于可移植性的原因,这可能不是一个好主意,但我仍然很好奇。

r2e*_*ans 5

有时方法重载是一件好事;data.table:::$<-.data.table对于原始函数,对于某些类(例如,和)是可行的tibble:::$.tbl_df,对于基类则不是微不足道的。总的来说,我认为尝试这样做是一个坏主意。

  • 一种惯用的方法是提供支持它的 S3 方法,例如$.numeric. 这将允许您严格控制此方法对特定类型对象的使用(在本例中,向量numeric ,不会在lists 上触发)。不幸的是,因为$是一个原始函数,所以它不允许在基本 R 对象类(例如$.numeric.

  • 如果您愿意重新分类要应用此功能的向量,则可以这样做:

    `$.quux` <- function(x, name) x[name]
    vec <- c(a = 1)
    class(vec) <- c("quux", class(vec))
    vec$a
    # a 
    # 1 
    vec$b
    # <NA> 
    #   NA 
    
    Run Code Online (Sandbox Code Playgroud)

    不幸的是,这需要您重新创建class任何您想要执行此操作的对象。

  • 另一种选择是重写$自身:

    `$` <- function(x, name) x[deparse(substitute(name))]
    c(a=1)$a
    # a 
    # 1 
    
    Run Code Online (Sandbox Code Playgroud)

    但这有很多问题:它影响 的每个正常使用$,包括非向量参数(尝试mtcars$mpg看看它现在返回单列data.frame而不是向量的正常行为)和赋值(mtcars$mpg <- ...失败)。当然可以尝试捕获每一种特殊情况,但您总是会错过一些极端情况、某些对象类型,或者导致一些其他假设的行为出现错误,从而破坏其他事情。

虽然我同意这种行为可能看起来有点不一致,但老实说,有时改变这种行为会产生太多的二阶效应,超出了创可贴的作用。(与这种变化的一个密切的类比是 python2 与 python3:这种“过渡”始于 2008 年 12 月,随着 python-3 的第一个版本的发布,尽管 python-2 据称已于 2020 年1 月终止生命,但它并没有迅速也不顺利。)