В 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
приходит на ум. В целом это считается очень плохой практикой. Это не то, что мы не хотим дать вам возможность продемонстрировать свои передовые знания языка, что в один прекрасный день нам придется иметь дело с этим кодом, и мы всего лишь смертные.
Так что сохраняйте свои программные трюки от взлома ночных сборок и дайте переменным разумные имена, с последовательным корпусом, если вы чувствуете себя более теплой и нечеткой.