На самом деле с функциями в R мы уже знакомы. Когда мы создаем вектор, мы берем функцию c()
, когда мы определяем максимальное значение в векторе, вызываем функцию max()
, когда выводим сообщение на экран, используем print()
или cat()
.
Но часто бывает, что встроенных функций R не хватает для полноценной работы, поэтому приходится дописывать некоторые функции самим.
При создании функции нужно учитывать три момента:
Для примера напишем свою функцию, которая будет принимать на вход числовой вектор и считать его среднее значение. Для простоты будем считать, что пропущенных значений в векторе нет. Создадим функцию my_mean()
.
Важно: своим функциям нужно давать названия, отличные от названий функций, уже встроенных в R. В случае совпадения R не выдаст ошибку, сохранит функцию с тем же названием, но потом могут возникнуть проблемы, да и самим легко запутаться.
Создание функции в R несильно отличается от создания функции в Python. Разница лишь в синтаксисе: здесь вместо оператора def
мы используем функцию function()
, которая сообщает R, объект какого типа мы создаем, плюс, в строке с результатом функции return()
тоже является функцией, а не оператором.
my_mean <- function(x){
avg <- sum(x) / length(x)
return(avg)
}
Применим функцию к вектору test1
и сохраняем результат в переменную r1
:
test1 <- c(10, 12, 45)
r1 <- my_mean(test1)
r1
## [1] 22.33333
Все работает. На самом деле, можно было обойтись и без return()
: R по умолчанию возвращает то, что указано в последней строчке в теле функции (в фигурных скобках).
my_mean <- function(x){
avg <- sum(x) / length(x)
avg
}
Тоже все работает:
my_mean(test1)
## [1] 22.33333
Главное не забыть важную вещь: в R, как и в Python (да и вообще в программировании) действие «функция возвращает какое-то значение» отличается от действия «функция выводит на экран какое-то значение»Функция может просто выводить результат на экран и никуда его не сохранять, а может, наоборот, сохранять результат, но на экран его не выводить.
Например, эта функция просто выводит среднее на экран, не сохраняя его:
my_mean2 <- function(x){
avg <- sum(x) / length(x)
cat(avg)
}
Результат выводится на экран, но не сохраняется:
my_mean2(test1)
## 22.33333
# сохранили в переменную и увидели,
# что там пустой объект типа NULL
r2 <- my_mean2(test1)
## 22.33333
r2
## NULL
Здесь нас может спасти функция print()
. Как мы обсуждали, эта функция не просто выводит объект на экран, но и сохраняет его. Проверим:
my_mean3 <- function(x){
avg <- sum(x) / length(x)
print(avg)
}
Результат выводится на экран:
my_mean3(test1)
## [1] 22.33333
И сохраняется!
r3 <- my_mean3(test1)
## [1] 22.33333
r3
## [1] 22.33333
Можно привести такую аналогию. Если я что-то говорю и не записываю, что говорю – я просто вывожу нечто на экран с помощью cat()
: результат все видят, но обратиться к нему позже не получится. Если я по ходу рассказа делаю некоторые заметки – я возвращаю определенный результат, сохраняю записи, которые позже можно будет прочитать.
Однако никто не мешает сочетать и основной результат, и какие-то побочные действия. Напишем «внимательную» функцию для среднего, которая будет считать среднее и напоминать нам, что нужно спать.
my_considerate_mean <- function(x){
avg <- sum(x) / length(x)
cat("You have to go to sleep!")
return(avg)
}
# и основной результат, и сообщение на экране
r4 <- my_considerate_mean(test1)
## You have to go to sleep!
r4
## [1] 22.33333
Напоследок обсудим еще одно отличие функций в R от функций в Python. Так как R не умеет автоматически объединять перечисленные через запятую объекты в какие-то структуры (Python запись 2, 3, 4
превратит в кортеж (2, 3, 4)
), функции в R не умеют возвращать несколько объектов в return()
. Их необходимо самим объединять либо в вектор, либо в список, либо в какую-то еще структуру.
Проверим: напишем функцию my_power()
, которая принимает на вход два целых числа a
и b
и возвращает пару чисел \(a^b\) и \(b^a\).
my_power <- function(a, b){
return(a ** b, b * a)
}
Функция создана, но она не работает (ошибка многоаргументные возвраты не разрешены):
my_power(2, 3)
Объединим пару результатов в вектор:
my_power <- function(a, b){
return(c(a ** b, b * a))
}
my_power(2, 3)
## [1] 8 6
Ура! Все работает!
Как быть, если функция выглядит громоздко, или мы создали слишком много полезных функций, и нам неудобно хранить их в файле с кодом? Можно вынести все необходимые написанные нами функции в отдельный файл с расширением .R
.
Скопируем для примера нашу функцию my_mean()
в новый файл и назовем его my_functions.R
. Для чистоты эксперимента откроем R заново и попробуем загрузить функцию из этого файла:
source("my_functions.R")
w <- c(0, 3, 7)
# работает, и R знает, откуда эта "не родная" функция взялась
my_mean(w)