В R, в чем именно проблема наличия переменных с тем же именем, что и у базовых функций R? - PullRequest
46 голосов
/ 26 мая 2011

Обычно считается плохой практикой программирования использовать имена переменных, которые имеют функции в базе 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?

Ответы [ 7 ]

23 голосов
/ 26 мая 2011

На самом деле не один. 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 против TRUE с проблемой маскировки функций. Поскольку T и TRUE не являются функциями, которые немного выходят за рамки вопроса @ Андри.

18 голосов
/ 26 мая 2011

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

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 для включения примера запутанных сообщений об ошибках

11 голосов
/ 26 мая 2011

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

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

Что просто относится fun к x. Не самый красивый способ сделать это, но вы можете столкнуться с этим из сценария кто-то или около того. Это работает для 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, которое может сломать столько скриптов.

3 голосов
/ 26 мая 2011

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

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

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

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

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

3 голосов
/ 26 мая 2011

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

1 голос
/ 26 мая 2011

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

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

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

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

0 голосов
/ 26 мая 2011

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

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

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