Замыкания с состоянием
Часть раздела Логика и потоки выполнения путешествия по R на Coddy. Урок 30 из 64.
Внутри функции <- создаёт локальную переменную. Оператор <<- вместо этого присваивает значение переменной с тем же именем во внешней функции, поэтому замыкание может обновлять захваченное им значение:
make_counter <- function() {
count <- 0
function() {
count <<- count + 1
count
}
}
next_id <- make_counter()
print(next_id())
print(next_id())
print(next_id())Вывод:
[1] 1
[1] 2
[1] 3У каждого счётчика, созданного создателем, есть собственный count, поэтому они никогда не влияют друг на друга:
make_counter <- function() {
count <- 0
function() {
count <<- count + 1
count
}
}
a <- make_counter()
b <- make_counter()
for (i in 1:2) a()
print(a())
print(b())Вывод:
[1] 3
[1] 1При использовании обычного <- внутри замыкания обновление создаёт новую локальную переменную, которая исчезает после каждого вызова, поэтому сохранённое значение никогда не изменяется:
make_broken <- function() {
count <- 0
function() {
count <- count + 1
count
}
}
f <- make_broken()
for (i in 1:2) f()
print(f())Вывод:
[1] 1Создатель может возвращать список замыканий, которые используют одни и те же переменные. Вместе они ведут себя как небольшой объект со своими данными:
make_wallet <- function(balance) {
list(
add = function(x) balance <<- balance + x,
get = function() balance
)
}
w <- make_wallet(10)
w$add(5)
print(w$get())Вывод:
[1] 15Задание
ЛегкоЗавершите make_counter(). Она возвращает функцию, которая при каждом вызове увеличивает свой собственный счётчик на 1 и возвращает новое значение счётчика, начиная с 1.
Предоставленный код создаёт два счётчика: a и b. Он считывает разделённый запятыми список из a и b, для каждой записи вызывает соответствующий счётчик и выводит каждое возвращённое значение в отдельной строке.
Попробуйте сами
make_counter <- function() {
# Напишите свой код здесь
function() 0
}
# Предоставленный код ввода/вывода: оставьте его как есть
input <- suppressWarnings(readLines(file("stdin")))
counters <- list(a = make_counter(), b = make_counter())
for (k in strsplit(input[1], ",")[[1]]) cat(counters[[k]](), "\n", sep = "")
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Логика и потоки выполнения
1Строки углублённо
Подстроки с substr()Форматирование с sprintf()Разделение и объединениеПоиск в строкахЗамена текстаПовторение — создание имени пользователя4Матрицы
Создание матрицИндексация матрицСводные данные по строкам и столбцамАрифметика матрицПовторение — схема рассадки7Семейство Apply
lapply и sapplyMap и mapplyFilter и FindReduceПовторение — конвейер обработки данных10Продвинутые конструкции управления
Функция switch()Векторизованный ifelse()repeat и breakРекурсивные функцииПовторение — классификатор оценок2Поиск по ключам и значениям
Поиск в именованных векторахПроверка ключейДобавление и удаление ключейПеребор имёнПовторение — биржа акций5Проект — Журнал оценок
Добавление учениковВыставление оценок3Множества и подсчёт
Уникальные значенияОперации с множествамиПроверка принадлежностиПодсчёт с помощью table()Повторение — гости мероприятия6Функции как значения
Анонимные функцииПередача функцийВозврат функцийЗамыкания с состояниемПовторение — правила скидок9Таблицы данных
Создание таблиц данныхСтолбцы и строкиФильтрация строкДобавление и сортировкаПовторение — отчёт о продажах12Проект — Трекер расходов
Запись расходовОбщие расходыПотренируйтесь самостоятельно: Онлайн-компилятор R