В R, что конкретно представляет собой проблема с переменными с тем же именем, что и базовые функции R?

Кажется, что в целом считается плохой практикой программирования, чтобы использовать имена переменных, которые имеют функции в базе R с тем же именем.

Например, возникает соблазн написать:

data <- data.frame(...)
df   <- data.frame(...)

Теперь функция data загружает наборы данных, а функция df вычисляет функцию плотности f.

Точно так же возникает соблазн написать:

a <- 1
b <- 2
c <- 3

Это считается плохой формой, потому что функция c будет объединять свои аргументы.

Но: В этой рабочей лошади R-функций lm для вычисления линейных моделей в качестве аргумента используется data. Другими словами, data становится явной переменной внутри функции lm.

Итак: Если основная команда R может использовать одинаковые имена для переменных и функций, что останавливает нас простых смертных?

Ответ не в том, что R запутается. Попробуйте следующий пример, где я явно назначаю переменную с именем c. R не путается вообще с разницей между переменной и функцией:

c("A", "B")
[1] "A" "B"

c <- c("Some text", "Second", "Third")
c(1, 3, 5)
[1] 1 3 5

c[3]
[1] "Third"

Вопрос: В чем проблема с наличием переменной с тем же именем, что и базовая функция R?

Ответ 1

На самом деле нет. При поиске функции R обычно не ищет объекты (объекты без функции):

> mean(1:10)
[1] 5.5
> mean <- 1
> mean(1:10)
[1] 5.5
> rm(mean)
> mean(1:10)
[1] 5.5

Примеры, показанные @Joris и @Sacha, - это то, где плохое кодирование улавливает вас. Один лучший способ написать foo:

foo <- function(x, fun) {
    fun <- match.fun(fun)
    fun(x)
}

Что при использовании дает:

> foo(1:10, mean)
[1] 5.5
> mean <- 1
> foo(1:10, mean)
[1] 5.5

Есть ситуации, когда это вас уловит, а пример @Joris с na.omit - это один, который IIRC, происходит из-за стандартной нестандартной оценки, используемой в lm().

Несколько ответов также связаны с проблемой T vs TRUE с маскировкой проблем с функциями. Поскольку T и TRUE не являются функциями, которые немного выходят за рамки вопроса @Andrie.

Ответ 2

Проблема заключается не столько в компьютере, сколько в пользователе. В общем, код может стать намного сложнее отладить. Опечатки сделаны очень легко, поэтому если вы делаете:

c <- c("Some text", "Second", "Third")
c[3]
c(3)

Вы получите правильные результаты. Но если вы пропустите что-то в коде и введите c(3) вместо c[3], найти ошибку будет не так просто.

Сфера охвата также может привести к очень запутывающим сообщениям об ошибках. Возьмите следующую ошибочную функцию:

my.foo <- function(x){
    if(x) c <- 1
    c + 1
}

> my.foo(TRUE)
[1] 2
> my.foo(FALSE)
Error in c + 1 : non-numeric argument to binary operator

С более сложными функциями это может привести вас к отладочной цепочке, ведущей в никуда. Если вы замените c на x в приведенной выше функции, ошибка будет читать "object 'x' not found". Это приведет намного быстрее к вашей ошибке кодирования.

Кроме того, это может привести к довольно запутанному коду. Код типа c(c+c(a,b,c)) запрашивает больше из мозга, чем c(d+c(a,b,d)). Опять же, это тривиальный пример, но он может изменить ситуацию.

И, очевидно, вы также можете получить ошибки. Когда вы ожидаете функцию, вы ее не получите, что может привести к еще одному набору раздражающих ошибок:

my.foo <- function(x,fun) fun(x)
my.foo(1,sum)
[1] 1
my.foo(1,c)
Error in my.foo(1, c) : could not find function "fun"

Более реалистичный (и реальный) пример того, как это может вызвать проблемы:

x <- c(1:10,NA)
y <- c(NA,1:10)
lm(x~y,na.action=na.omit)
# ... correct output ...
na.omit <- TRUE
lm(x~y,na.action=na.omit)
Error in model.frame.default(formula = x ~ y, na.action = na.omit, 
drop.unused.levels = TRUE) : attempt to apply non-function

Попробуйте выяснить, что здесь не так, если na.omit <- TRUE происходит 50 строк в вашем коде...

Ответ отредактирован после комментария @Andrie, чтобы включить пример путающих отчетов об ошибках

Ответ 3

R очень устойчив к этому, но вы можете придумать, как его сломать. Например, рассмотрим эту функцию:

foo <- function(x,fun) fun(x)

Что просто применяет fun к x. Не самый красивый способ сделать это, но вы можете столкнуться с этим от someones script или около того. Это работает для mean():

> foo(1:10,mean)
[1] 5.5

Но если я назначаю новое значение, чтобы оно разрывалось:

mean <- 1
foo(1:10,mean)

Error in foo(1:10, mean) : could not find function "fun"

Это произойдет очень редко, но это может произойти. Это также очень смущает людей, если одно и то же означает две вещи:

mean(mean)

Так как тривиально использовать любое другое имя, которое вы хотите, почему бы не использовать другое имя, а затем базовые функции R? Кроме того, для некоторых R-переменных это становится еще более важным. Подумайте о переназначении функции '+'! Другим хорошим примером является переназначение T и F, которое может сломать так много скриптов.

Ответ 4

Я думаю, проблема в том, что люди используют эти функции в глобальной среде и могут вызвать разочарование из-за неожиданной ошибки, которую вы не должны получать. Представьте, что вы просто запускали воспроизводимый пример (может быть, довольно длинный), который переписывал одну из функций, которые вы используете в симуляции, которая занимает много времени, чтобы добраться туда, где вы хотите, а затем внезапно она разбивается со смешной ошибкой. Использование уже существующих имен функций для переменных в закрытой среде (например, функция) удаляются после закрытия функции и не должны наносить вреда. Предполагая, что программист осознает все последствия такого поведения.

Ответ 5

Ответ прост. Ну, вроде.

Суть в том, что вам следует избегать путаницы. Технически нет оснований указывать собственные имена переменных, но это упрощает чтение кода.

Представьте, что у вас есть строка кода, содержащая что-то вроде data()[1] или подобное (эта строка, вероятно, не имеет смысла, но это только пример): хотя теперь вам ясно, что вы используете данные функции здесь, читатель, который заметил, что там есть data.frame с именем data, может быть запутан.

И если вы не настроены альтруистично, помните, что читатель мог быть вам через полгода, пытаясь понять, что вы делаете с этим старым кодом.

Возьмите его у человека, который научился использовать длинные имена переменных и соглашения об именах: он окупается!

Ответ 6

Я согласен с @Gavin Simpson и @Nick Sabbe, что на самом деле нет проблемы, но это скорее вопрос читаемости кода. Следовательно, как много вещей в жизни, это вопрос конвенции и консенсуса.

И я думаю, что это хорошая конвенция, чтобы дать общий совет: не называйте ваши переменные такими, как базовые функции R!

Этот совет работает, как и другие полезные советы. Например, мы все знаем, что мы не будем пить слишком много выпивки и не ешьте слишком много нездоровой пищи, но время от времени мы не можем следовать этим советам и напиваться, употребляя слишком много нездоровой пищи.

То же самое верно для этого совета. Очевидно, имеет смысл назвать аргумент данных data. Но гораздо меньше смысла называть вектор данных mean. Хотя могут быть ситуации, в которых даже это кажется уместным. Но старайтесь избегать этих ситуаций для ясности.

Ответ 7

Хотя некоторые языки могут это позволить, IF IF THEN THEN ELSE ELSE приходит на ум. В целом это считается очень плохой практикой. Это не то, что мы не хотим дать вам возможность продемонстрировать свои передовые знания языка, что в один прекрасный день нам придется иметь дело с этим кодом, и мы всего лишь смертные.

Так что сохраняйте свои программные трюки от взлома ночных сборок и дайте переменным разумные имена, с последовательным корпусом, если вы чувствуете себя более теплой и нечеткой.