Перегрузка `$` для именованного вектора в R - PullRequest
5 голосов
/ 04 ноября 2019

При работе с именованными векторами, такими как

vec <- c(a = 1, b = 2)

, я часто сталкиваюсь с ошибками, когда пишу vec$a, когда мне нужно написать vec["a"] или vec[["a"]] для доступа к соответствующему значению с и безимя соответственно.

Я думаю, vec$a быть ошибкой нелогично, поскольку $ обычно извлекает именованные вещи. Это чувство даже поддерживается, например, в ?Extract, где пример использования x$name - разве это не идеально подходит для с именем вектора?

Это заставило меня задуматься овозможность перегрузки оператора $ для работы с именованными векторами. Однако я не очень опытен с перегрузкой операторов в R, и я понимаю (например, из ответов здесь ), что рекомендуется соблюдать осторожность при перегрузке основных операторов.

Мои взаимосвязанные вопросы: Есть ли причина, по которой я не должен перегружать $, как описано , которую я не понимаю? То есть есть (в некотором неопределенном смысле) "хорошая" причина, почему это не значение по умолчанию в R ? Если нет, как я могу разумно сделать это ?

Я понимаю, что на практике это, вероятно, плохая идея, хотя бы по причинам переносимости, но мне все еще любопытно.

1 Ответ

4 голосов
/ 04 ноября 2019

Бывают случаи, когда перегрузка метода - хорошая вещь;с примитивными функциями это возможно с некоторыми классами (например, data.table:::$<-.data.table и tibble:::$.tbl_df), это не тривиально с базовыми классами. В общем, я думаю, что пытаться это плохая идея.

  • Один идиоматический способ сделать это - предоставить метод S3, который его поддерживает, такой как $.numeric. Это позволило бы вам жестко контролировать использование этого метода для объекта определенного типа (в этом случае numeric vector , не срабатывает при list s). К сожалению, поскольку $ является примитивной функцией, она не допускает перегрузки базовых классов объектов 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 
    

    К сожалению, это требует от вас повторно class любого объекта, с которым вы хотите сделать это.

  • Другим вариантом было бы переопределить само $:

    `$` <- function(x, name) x[deparse(substitute(name))]
    c(a=1)$a
    # a 
    # 1 
    

    Но есть очень много проблем с этим: это влияеткаждое обычное использование $, включая не-векторные аргументы (попробуйте mtcars$mpg и посмотрите, что теперь он возвращает одиночный столбец data.frame вместо нормального поведения вектора) и присваивание (mtcars$mpg <- ... не выполняется). Конечно, можно попытаться поймать каждый из этих особых случаев, но неизменно вы либо пропустите какой-то угловой случай, какой-либо тип объекта, либо заставите какое-то предполагаемое поведение неправильно себя вести, нарушая другие вещи.

Хотя я согласен с тем, что это поведение может показаться немного противоречивым, честно говоря, наступает момент, когда изменение этого типа поведения имеет слишком много эффектов второго порядка, чем вы можете нанести бинты. (Одной близкой аналогией такого рода изменений может быть python2 по сравнению с python3: этот «переход» начался в декабре 2008 года с первого выпуска python-3, и, несмотря на предполагаемый конец срока службы python-2 в январе 2020 года , это не было ни быстро, ни гладко.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...