Стилевой гид это не просто негласный договор разработчиков – за многими из правил стоит любопытная предыстория. Почему стрелка
<- лучше знака равенства =, почему старожилы R не любят нижнее подчеркивание, как рекомендуемая длина строки связана с перфокартой, и о многом другом – далее.Тем не менее, гид Хэдли можно считать самым распространённым (как встроенная проверка RStudio, например), чему в немалой степени способствует популярность библиотек, созданных самим Хэдли (dplyr, ggplot, tidyr, и других из коллекции tidyverse).
1. Оператор присваивания: <- vs =
Все доступные гиды рекомендуют использовать нестандартный оператор
<-, а вовсе не знак равенства =, привычный для других современных языков. Три других оператора (<<-, ->, ->>) даже не упоминаются (как и существовавший в ранних версиях :=). Казалось бы, зачем нужна эта нестандартная стрелка?История приоткрывает нам карты: в R стрелка пришла из S, который в свою очередь, унаследовал её от APL. В языке APL она позволяла отличить присвоение от равенства. В R оператор равенства стандартный, так что разница в другом. Если стрелка была оператором присвоения изначально, то знак равенства присваивал значения только именованным параметрам. В 2001 году знак равенства стал оператором присвоения, но так и не стал синонимом стрелки.
Что же позволяет считать
= полноценной заменой стрелке? Прежде всего, = как оператор присвоения работает только на верхнем уровне. Например, внутри функции всё будет работать по-старому:mean(x = 1:5)
# [1] 3
x
# Error: object 'x' not found
mean (x <- 1:5)
# [1] 3
x
# [1] 1 2 3 4 5
Здесь
= только задаёт параметр функции, в то время как <- ещё и присваивает значение переменной x. Мы можем добиться такого же эффекта, поместив операцию присваивания в скобки mean ((x = 1:5))
# [1] 3
x
# [1] 1 2 3 4 5
… или в фигурные скобки:
mean ({x = 1:5})
# [1] 3
x
# [1] 1 2 3 4 5
Помимо этого, стрелка имеет приоритет над знаком равенства:
x <- y <- 1
# OK
x = y = 2
# OK
x = y <- 3
# OK
x <- y = 4
# Error in x <- y = 4 : could not find function "<-<-"
Последнее выражение завершилось ошибкой потому что эквивалентно
(x <- y) = 4, и парсер интерпретирует его как`<-<-`(x, y = 4, value = 4)
Иными словами, мы пытаемся выполнить некорректную операцию: сначала присваиваем x значение y, а затем пытаемся присвоить x и y значение 4. Выражение будет обработано без ошибок только если поменять приоритет операций скобками:
x <- (y = 4).2. Spacing
Гид рекомендует ставить пробелы между операторами (кроме, естественно, квадратных скобок, :, :: и :::), а также перед открывающей скобкой. Вполне очевидно, что это часть стандартов кодирования GNU. Впрочем, этот пункт тесно связан с использованием
<- как оператора присвоения. Например,x <-1Что это? X меньше -1? Или присвоить x значение 1?
Впрочем, лишний пробел ничуть не лучше недостающего, например:
x <- 0
ifelse(x <-1, T, F)
# [1] TRUE
x <- 0
ifelse(x < -1, T, F)
# [1] FALSE
В первом случае нет пробела между
< и -, что создаёт оператор присваивания.3. Имена функций и переменных
В вопросе имён стилевые руководства расходятся: гид Хэдли рекомендует нижнее подчёркивание для всех имен; Гид Google – разделение точками для переменных и стиль верблюда с первой строчной для функций; Bioconductor рекомендует lowerCamel как для функций, так и для переменных. В этом вопросе в сообществе R нет единства, и можно встретить все возможные стили:
lowerCamel
period.separation
lower_case_with_underscores
allowercase
UpperCamel
Единого стиля нет даже именах base R (например, rownames и row.names это разные функции!). Если не брать во внимание нечитабельный allowercase (его могут любить только пользователи Matlab), можно выделить три самых популярных стиля: lowerCamel, нижний регистр с _, и нижний регистр с разделением точками.

Популярность разных стилей для названий функций и параметров (одно название может отвечать разным стилям). Источник: выступление Rasmus Baath на useR!2017.
Разделение точками зловеще напоминает использование методов в объектно-ориентированном программировании, но исторически распространено. Настолько распространено, что именно этот стиль можно считать истинно R’вским. Например, большинство базовых функций используют именно его (а с data.table и as.factor точно встречался каждый).
А вот разделение _ является одним из наименее популярных стилей (и здесь Хэдли идёт против большинства). Для многих пользователей R нижнее подчёркивание будет раздражителем: в популярном расширении Emacs Speaks Statistics оно по умолчанию заменяется на оператор присвоения
<-. А настройки по умолчанию, конечно же, Впрочем, влияние Emacs ESS – это всё ещё объяснение из разряда «хвост виляет собакой». Есть и более древняя причина: в ранних версиях R нижнее подчёркивание было синонимом стрелки
<-. Например, в 2000 году можно было встретить такое:# имело место в ранних версиях R
c <- c(1,2,3,4,5)
mean(c)
[1] 3
c_mean <- mean(c)
c
[1] 3
Здесь вместо того, чтобы создать переменную
c_mean R присвоил значение 3 сначала переменной mean, а затем переменной c. В современном R такие метаморфозы, конечно же, происходить не будут.Из-за непопулярности _ функции такого стиля почти не встречаются среди базовых:
# в 3.5.1 всего 25 функций
grep("^[^\\.]*$", apropos("_"), value = T)
Наконец, стиль lowerCamel отличается низкой читабельностью при использовании длинных имён:
# ой!
GrossNationalIncomePerCapitaAtlasMethodCurrentUnitedStatesDollars
Таким образом, в плане имён рекомендации гида нельзя считать однозначными; всё-таки это дело вкуса (до тех пор пока в этом наблюдается последовательность).
4. Фигурные скобки
Согласно гиду, после открывающей фигурной скобки должна следовать новая строка, а закрывающая находиться на отдельной строке (если только за ней не следует else). Т.е. примерно так:
if (x >= 0) {
log(x)
} else {
message("Not applicable!")
}Здесь всё не очень интересно: это стандартный стиль отступов K&R, восходящий к языку C и знаменитой книге Кернигана и Ритчи «The C Programming Language» (или K&R по именам авторов).
Истоки этого стиля тоже вполне очевидны: он позволяет экономить строки, сохраняя при этом читабельность. Для ранних компьютеров вертикальное пространство было слишком большой роскошью. Например, C был разработан на PDP-11, в терминале которого было всего 24 строки. Да и при печати книги K&R этот стиль экономил бумагу!
5. Строка на 80 символов
Рекомендуемая длина строки согласно гиду – 80 символов. Волшебное число 80 встречаются не только в R, но и огромном количестве других языков (Java, Perl, PHP, и т.д. и т.п.). И не только языков: даже командная строка Windows состоит из 80 символов.
Впервые в программировании это число появилось в 1928 г. вместо со стандартной перфокартой IBM, где было ровно 80 колонок для данных. Куда более интересный вопрос – почему был выбран именно такой стандарт? Ведь ранее использовались и перфокарты другой длины (на 24 или 45 колонок).

Самый популярный ответ связывает длину перфокарты с длиной строки у печатных машинок. Первые машинки были рассчитаны на американский стандарт бумаги 8? x 11 дюймов, и позволяли печатать от 72 до 90 символов, в зависимости от размера полей. Поэтому версия о 80 символах в строке выглядит вполне правдоподобной, хотя и не правдой в последней инстанции. Вполне возможно, что 80 символов – это просто золотая середина в плане эргономики.
6. Отступ строки: пробелы vs табуляция
Рекомендуемый гидом стиль – это два пробела, а не табуляция. Отказ от табуляции вполне понятен: длина TAB’а варьируется в разных текстовых редакторах (это может быть что угодно от 2 до 8 пробелов). Отказываясь о них, мы получаем сразу два преимущества: во-первых, код будет выглядеть точно также, как мы его набрали; во-вторых, не произойдёт случайного нарушения рекомендуемой длины строки. При этом мы, конечно же, увеличиваем размер файла (кто хочет заниматься такой микрооптимизаций в 2k19 году?)
Спор пробелы vs табуляция имеет давнюю историю, и может приравниваться к религиозным (как, например, Win vs Linux, Android vs iOS, и тому подобные). Впрочем, мы уже знаем, кто в нём победил: согласно исследованию Stack Overflow, разработчики, использующие пробелы, зарабатывают больше, чем те, кто использует табуляцию. Более весомый аргумент, чем правила стилевого гида, не так ли?
Вместо заключения: правила стилевых гидов могут казаться странными и нелогичными. Действительно, зачем стрелка
<-, если есть стандартный оператор =? Но если копнуть глубже, то за каждым правилом стоит некоторая логика, часто уже подзабытая. Комментарии (8)

hombre
02.06.2019 15:11Из-за непопулярности _ функции такого стиля почти не встречаются среди базовых:
Да и мучительно привыкать к написанию seq_along()
с оператором %>% тоже регулярно путаница в dplyr и tidyverse

SegreyBorovkov
02.06.2019 16:46Объясните, пожалуйста, в чем смысл использования
<-вместо=? По мне — написать что-то типаfunc_name(x <- 5)— хороший повод получить канделябром от коллеги, ибо не особо очевидно зачем так делать. А если так не делать, то почему не использовать повсеместно просто знак равенства?
Если посмотреть кернелы кагла, видно, что там R — в районе 5-10%. Для меня это показатель, что человек, занимающийся анализом данных на R и не желающий оказаться позади, должен как минимум понимать python. А если так — почему не использовать знак=для присвоения и в R, раз он работает в обоих языках?
demche Автор
02.06.2019 18:34В самом начале это давало обратную совместимость с S и S-PLUS. Сейчас особого смысла здесь нет: исключительно традиция. И поскольку R используется не только в машинном обучении, традиция вряд ли поменяется в обозримом будущем.
И да, внутри функции всё-таки лучше использовать=. Просто этот пример наглядно демонстрирует, что назначение параметра функции и присвоение значения — не одно и тоже. В вашем примереfunc_name(x <- 5)будет обработано всегда, а вотfunc_name(x = 5)только если x является параметром функции.
Cekory
02.06.2019 23:36Не совсем понимаю, что значит "
x <- 5будет обработано всегда". Присваивание с одновременной передачей параметра снижает читаемость и поэтому считается плохим тоном, его надо избегать. А вариант сx = 5упадет с ошибкой, еслиxне является параметром функции и это очень правильно. Сам я всегда стараюсь писать=, потому что никаких рациональных доводов в пользу стрелочки нет, только традиции. Против неё описанная вами ситуация со сравнениемx<-5. То есть, с моей точки зрения, 1:0 в пользу=.
demche Автор
03.06.2019 00:06Будет обработано всегда — в смысле, что внутри функции мы можем присвоить переменной какое угодно имя, и ошибки не будет. Например,
mean(some_var_name <- 1:5)будет обработано корректно, аmean(some_var_name = 1:5)выдаст ошибку т.к. название аргумента у этой функции х.
(Сам не одобряю практику использования стрелки внутри функции, просто как иллюстрация возможности)
Cekory
03.06.2019 00:12С моей точки зрения корректное поведение — выдача ошибки. Допустим, есть такая функция:
f = function(x = NA, y = NA) {cat("x = ", x, "y = ", y, "\n")}, тогдаf(y <- 25)выдастx = 25 y = NA. Понятно, почему так происходит, но это явно не про читаемость.

BkmzSpb
Честно говоря, особенности
Rпроявляются только в части пунктов. Расположение фигурных скобок, спейсинг и пробелы vs табуляция порождают конфликты вокруг, кажется, любого языка. Но хотелось бы добавить несколько вещей.1) Псевдооператор
`:=`из пакетов семестваtidy. Вызов оператора отваливается с ошибкой, но он по-особому интерпретируется механизмом цитированияrlangи по сути означает присваивание:2) По-хорошему, использование оператора
<-в списке аргументов функций должено считаться плохой практикой.<-используется для присваивания значений в текущем окружении, поэтому проблемы можно получить в редком случае, когдаx <- 5записан какx < -5. К сожалению, синтаксис валидный и ошибки инетрпретации вы не получите. В качестве альтернативы можно окунуться в функциональный подход. Тогда присваивание будет последним действием:x %>% do_job %>% do_more(with_param) -> y, но с точки зрения читабельности возникают вопросы.3) Имена функций и переменных. Честно говоря, базовые пакеты, написанные статистиками, это какой-то ад. Конвенции попросту отсутствуют, а запомнить какие-то особые аргументы просто невозможно (мне вспоминается дурацкий
na.rm, который я вечно пишу какrm.na). Имена с точками, которые есть и в базовом пакете, могут привести к проблемам с дженериками, т.к..class_nameпостфикс используется для указания типа. Например, стандартныйdata.frameимеет оператор преобразования as:as.data.frame, в соответствии с соглашением обisиasоператорах. Так вот, если применить этот метод кdata.frame, то на деле вызовется…as.data.frame.data.frame, и это валидная существующая функция, которую вы можете проверить.Собственно это одна из причин, по которой
tidyverseиспользует lowercase с underscore.demche Автор
С этим полностью согласен: глобального присвоения следует избегать. Поэтому оправданной будет конвенция:=был оператором присвоения только в ранних версиях R. Сейчас в base такого оператора больше нет, а значит его вызов и не должен работать. Но токен:=остался в коде R какLEFT_ASSIGN(см. source code).А раз есть токен, то
:=можно использовать как инфиксный оператор, не прибегая к формату%function_name%. Таким образом, его использование в коллекции tidy правомерное, хотя это своего рода хак. Да и не только там:=встречается: в data.table он тоже есть.=для параметров функции,<-для всего остального.