---
title: "Основы программирования в R"
subtitle: "Функции в R"
author: "Алла Тамбовцева, НИУ ВШЭ"
output:
  pdf_document:
    latex_engine: xelatex
mainfont: CMU Serif
header-includes:
- \usepackage[russian]{babel}
- \usepackage{hyperref}
- \hypersetup{colorlinks = true, urlcolor = blue, linkcolor=magenta}
---

## Функции

На самом деле с функциями в R мы уже знакомы. Когда мы создаем вектор, мы берём функцию `c()`, когда мы определяем максимальное значение в векторе, вызываем функцию `max()`, когда выводим сообщение на экран, используем `print()` или `cat()`.

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

При создании функции нужно учитывать три момента:

* что функция получает на вход (аргументы или параметры функции);
* что функция делает;
* что функция возвращает, выдает на выходе (результат функции).

Для примера напишем свою функцию, которая будет принимать на вход числовой вектор и считать его среднее значение. Для простоты будем считать, что пропущенных значений в векторе нет. Создадим функцию `my_mean()`.

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

Создание функции в R несильно отличается от создания функции в Python. Разница лишь в синтаксисе: здесь вместо оператора `def` мы используем функцию `function()`, которая сообщает R, объект какого типа мы создаем, плюс, в строке с результатом функции `return()` тоже является функцией, а не оператором.

```{r}
my_mean <- function(x){
  avg <- sum(x) / length(x)
  return(avg)
}
```

Применим функцию к вектору `test1` и сохраняем результат в переменную `r1`:

```{r}
test1 <- c(3, 6, 7)
r1 <- my_mean(test1)
r1
```

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

```{r}
my_mean <- function(x){
  avg <- sum(x) / length(x)
  avg
}
```

Тоже все работает:

```{r}
my_mean(test1)
```

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

Например, эта функция просто выводит среднее на экран, не сохраняя его:

```{r}
my_mean2 <- function(x){
  avg <- sum(x) / length(x)
  cat(avg) 
}
```

Результат выводится на экран, но не сохраняется:

```{r}
my_mean2(test1) 
```

```{r}
r2 <- my_mean2(test1) 
r2
```

Здесь нас может спасти функция `print()`. Как мы обсуждали, эта функция не просто выводит объект на экран, но и сохраняет его. Проверим:

```{r}
my_mean3 <- function(x){
  avg <- sum(x) / length(x)
  print(avg) 
}
```

Результат выводится на экран:

```{r}
my_mean3(test1)
```

И сохраняется!

```{r}
r3 <- my_mean3(test1)
r3
```

Можно привести такую аналогию. Если я что-то говорю и не записываю, что говорю – я просто вывожу нечто на экран с помощью `cat()`: результат все видят, но обратиться к нему позже не получится. Если я по ходу рассказа делаю некоторые заметки – я возвращаю опредёленный результат, сохраняю записи, которые позже можно будет прочитать.

Однако никто не мешает сочетать и основной результат, и какие-то побочные действия. Напишем «внимательную» функцию для среднего, которая будет считать среднее и напоминать нам, что нужно спать.

```{r}
my_considerate_mean <- function(x){
  avg <- sum(x) / length(x)
  cat("You have to go to sleep!")
  return(avg) 
}
```

Получаем и основной результат, и сообщение на экране:

```{r}
r4 <- my_considerate_mean(test1)
r4
```

Напоследок обсудим ещё одно отличие функций в R от функций в Python. Так как R не умеет автоматически объединять перечисленные через запятую объекты в какие-то структуры (Python запись 2, 3, 4 превратит в кортеж (2, 3, 4)), функции в R не умеют возвращать несколько объектов в `return()`. Их необходимо самим объединять либо в вектор, либо в список, либо в какую-то ещё структуру.

Проверим: напишем функцию `my_power()`, которая принимает на вход два целых числа `a` и `b` и возвращает пару чисел $a^b$ и $b^a$.

```{r}
my_power <- function(a, b){
  return(a ** b, b * a)
}
```

Функция создана, но она не работает (ошибка `многоаргументные возвраты не разрешены`):

```{r, eval=FALSE}
my_power(2, 5)
```

Объединим пару результатов в вектор:

```{r}
my_power <- function(a, b){
  return(c(a ** b, b * a))
}

my_power(2, 5)
```

Ура! Всё работает!

Если мы хотим сделать выдачу более *user friendly*, в `return()` можем записать поименованный вектор:

```{r}
my_power <- function(a, b){
  return(c(result1 = a ** b, result2 = b * a))
}

my_power(2, 5)
```

