Menu
Coddy logo textTech

Замыкания с состоянием

Часть раздела Логика и потоки выполнения путешествия по 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
challenge icon

Задание

Легко

Завершите 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 = "")
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Логика и потоки выполнения

Потренируйтесь самостоятельно: Онлайн-компилятор R