Menu

Функции в R: определение, аргументы и возвращаемые значения

Как создать функцию в R — синтаксис function(x) { }, возвращаемые значения, значения по умолчанию и именованные аргументы, многоточие ..., анонимные функции и работа областей видимости.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Определение и вызов функции

Функция в R — это значение, как число или строка. Вы строите её ключевым словом function и прикрепляете к имени через <-, точно так же, как сохранили бы что угодно другое в переменной:

Из чего состоит: function(x) объявляет один параметр с именем x, фигурные скобки содержат тело, а вызов square(4) выполняет тело с x, равным 4. Для однострочного тела фигурные скобки необязательны — square <- function(x) x^2 это та же функция, — но со скобками читается лучше, когда тело растёт.

Поскольку функция — просто значение, всё, что вы знаете о переменных, применимо и к ней: её можно переприсвоить, передать в другую функцию или сохранить в списке. Один этот факт питает половину идиоматичного R, включая всё семейство apply.

Возвращаемые значения: побеждает последнее выражение

Функция R возвращает то, что даёт её последнее вычисленное выражение. Обязательной инструкции return нет:

return() существует и работает так, как вы ожидаете: немедленно останавливает функцию и возвращает свой аргумент. Идиоматичный R приберегает его для досрочного выхода — прервать работу в начале, когда вход делает остальное бессмысленным. Сочетайте его с проверками if-else:

Написать return(a / b) на последней строке было бы не ошибкой, просто шумом. Приберегите return() для досрочных выходов, а нормальный путь пусть озвучивает последнее выражение.

Значения по умолчанию и именованные аргументы

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

Третий вызов показывает вторую половину истории — именованные аргументы. Когда вы пишете name = "Ada" в месте вызова, позиция перестаёт иметь значение: R сначала сопоставляет по имени, а затем заполняет оставшиеся параметры по позиции. Отсюда два практических правила:

  • Ставьте обязательные параметры первыми, а параметры со значениями по умолчанию — после них, чтобы позиционные вызовы оставались естественными.
  • В месте вызова именуйте любой аргумент, который не очевиден из позиции. greet("Ada", punctuation = "?") понятнее, чем заставлять читателя считать запятые.

Проброс аргументов через ...

Специальный параметр ... (произносится «многоточие») означает «любое число дополнительных аргументов». Функция, объявившая ..., может принимать аргументы, которые она никогда не перечисляла, и пробрасывать их в другую функцию:

shout() не знает и не интересуется, сколько слов вы передаёте, — она отдаёт их все в paste(). Обратите внимание, что punctuation идёт после многоточия: любой параметр, объявленный после ..., можно задать только по имени, а это ровно то, что нужно для опций. Такой шаблон проброса встречается в базовом R повсюду — sapply(x, round, digits = 2) работает потому, что sapply пробрасывает digits = 2 в round через собственное ....

Цена многоточия в том, что опечатки проскакивают молча: напишите punctation вместо punctuation — и это просто проглотится в ... вместо ошибки. Держите функции с многоточием небольшими.

Анонимные функции

Когда функция нужна лишь однажды — обычно как аргумент другой функции, — имя можно вообще пропустить:

Обе строки делают одно и то же. function(x) x^2 — классическое написание; \(x) x^2 — сокращение, добавленное в R 4.1, где \ заменяет слово function. Они идентичны во всём, кроме числа нажатий клавиш, — сокращение не является другим видом функции.

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

Области видимости: что видит функция

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

Обратная сторона: присваивание внутри функции никогда не просачивается наружу. rate <- 0.5 внутри change_inside создаёт свежую локальную переменную, которая умирает при возврате из функции; внешняя rate по-прежнему печатает 0.1. Это преимущество — функции не могут случайно затоптать ваше рабочее пространство, — но оно удивляет новичков, ожидающих, что внутреннее присваивание сохранится. Если функции нужно передать результат наружу, верните его и присвойте в месте вызова. (Существует оператор <<-, дотягивающийся наружу; считайте его признаком плохого кода, пока он вам действительно не понадобится.)

Разобранный пример

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

Здесь есть всё с этой страницы: параметр digits со значением по умолчанию, именованный вызов, который его переопределяет, и последнее выражение (round(out, digits)), выполняющее возврат. Функция читается как предложение, работает на любом числовом векторе и живёт в одном месте, когда вы решите добавить медиану.

Что вы уносите с собой

  • name <- function(args) { body } создаёт функцию; её возвращаемое значение — последнее вычисленное выражение.
  • return() — для досрочных выходов, а не для обычной последней строки.
  • Значения по умолчанию объявляются через = в списке параметров; именованные аргументы делают вызовы независимыми от порядка и читаемыми.
  • ... принимает и пробрасывает любые дополнительные аргументы — мощно, но скрывает опечатки.
  • function(x) и \(x) — одно и то же; используйте анонимные функции для разовых однострочников.
  • Функции читают переменные оттуда, где были определены; присваивания внутри них остаются внутри.

Дальше: семейство apply — функции, которые берут ваши функции и запускают их по целым векторам и спискам.

Часто задаваемые вопросы

Как создать функцию в R?

Присвойте объект function имени: square <- function(x) { x^2 }. Параметры идут в круглых скобках, тело — в фигурных, а вызываете вы её как square(4). Функция возвращает последнее вычисленное выражение.

Нужен ли return() в R?

Нет. Функция R автоматически возвращает значение последнего вычисленного выражения. return() существует, и идиоматичный R приберегает его для досрочного выхода — когда нужно прервать работу в начале функции, если вход некорректен или обработан краевой случай.

Что такое анонимная функция в R?

Функция, которую вы определяете, не давая ей имени, обычно передавая прямо в другую функцию: sapply(x, function(v) v^2). Начиная с R 4.1 есть также сокращение \(v) v^2, означающее ровно то же самое при меньшем числе нажатий клавиш.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ