Правила лексической области видимости
Разберитесь, как R находит переменные, выполняя поиск в родительских окружениях.
«Правила лексической области видимости» — бесплатный урок R Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения R Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс R Academy содержит 4 уроков всего.
Что такое лексическая область видимости
Лексическая область видимости означает, что функция ищет переменные там, где она была определена, а не там, откуда она вызвана. В R используется лексическая область видимости. Это делает поведение предсказуемым: окружение, в котором функция выполняет поиск, фиксируется при её создании, а не при вызове.
x <- 'global'
f <- function() {
cat('x =', x, '\n') # looks where f was defined (global)
}
local({
x <- 'local to local block'
f() # still prints global x, not the local one
})Лексическая и динамическая области видимости
При динамической области видимости (используемой в некоторых других языках) функция нашла бы переменную в окружении вызывающего кода. В R вместо этого используется лексическая область видимости: объемлющее окружение на момент определения функции всегда имеет приоритет. Поэтому функции ведут себя одинаково независимо от места вызова.
val <- 100
make_adder <- function(n) {
# n is captured from the definition environment
function(x) x + n
}
add10 <- make_adder(10)
add20 <- make_adder(20)
cat(add10(5), '\n') # 15 — n=10 from definition
cat(add20(5), '\n') # 25 — n=20 from definitionСвободные переменные и замыкания
Переменная внутри функции, которая не является параметром и не получает локального значения, называется свободной переменной. R ищет её в объемлющем окружении. Функция, захватывающая свободные переменные, называется замыканием.
base_rate <- 0.05 # free variable captured by f
f <- function(principal, years) {
principal * (1 + base_rate) ^ years
}
cat('5 yr return:', f(1000, 5), '\n')
# Even if we change base_rate later:
base_rate <- 0.08
cat('New rate return:', f(1000, 5), '\n')Замыкания захватывают окружения
При каждом вызове фабричной функции возвращаемая функция (замыкание) захватывает состояние объемлющего окружения на этот момент. Разные вызовы создают замыкания с разными захваченными значениями — на этом основаны фабрики функций.
power_fn <- function(exp) {
function(x) x ^ exp
}
square <- power_fn(2)
cube <- power_fn(3)
cat('square(4) =', square(4), '\n')
cat('cube(3) =', cube(3), '\n')Оператор <<-
<<- (присваивание двойной стрелкой) ищет существующую привязку по цепочке родительских окружений и изменяет её на месте. Если привязка не найдена, она создаётся в глобальном окружении. Этот оператор позволяет изменять переменную в объемлющей области видимости изнутри функции.
count <- 0
increment <- function() {
count <<- count + 1
}
increment()
increment()
increment()
cat('count:', count, '\n')<<- и замыкания
Наиболее идиоматично использовать <<- внутри замыканий, которым нужно обновлять состояние в объемлющем окружении. Так создаются функции с состоянием — функции, сохраняющие память между вызовами.
make_counter <- function() {
n <- 0
list(
increment = function() n <<- n + 1,
get = function() n,
reset = function() n <<- 0
)
}
ctr <- make_counter()
ctr$increment()
ctr$increment()
cat('Count:', ctr$get(), '\n')
ctr$reset()
cat('After reset:', ctr$get(), '\n')Область видимости во вложенных функциях
Когда функции вложены друг в друга, внутренняя функция может видеть переменные, определённые во внешней функции. Окружение внешней функции становится объемлющим окружением внутренней, образуя цепочку.
outer <- function() {
greeting <- 'Hello'
inner <- function(name) {
# greeting comes from outer's environment
paste(greeting, name)
}
inner('World')
}
cat(outer(), '\n')Поиск по цепочке областей видимости
При поиске переменной R проверяет окружения в таком порядке: собственное окружение функции → объемлющее окружение → окружение, объемлющее его → ... → глобальное окружение → путь поиска → базовое окружение → пустое окружение. Используется первое найденное совпадение.
x <- 'global'
f <- function() {
x <- 'local to f'
g <- function() {
# g has no x, so looks in f's env
cat('g sees x =', x, '\n')
}
g()
}
f()environment() функции
Объемлющее окружение любой функции можно изучить с помощью environment(f). Его даже можно изменить с помощью environment(f) <- e: тогда функция будет искать свободные переменные в другом месте. Это мощный, но продвинутый приём.
x <- 'global_x'
f <- function() x
cat('f sees:', f(), '\n')
# Change f's enclosing environment
my_env <- new.env(parent = emptyenv())
my_env$x <- 'env_x'
environment(f) <- my_env
cat('f now sees:', f(), '\n')Практика: замыкание для мемоизации
Лексическая область видимости и <<- позволяют выполнять мемоизацию: сохранять результаты ресурсоёмких функций в окружении замыкания. Кэш остаётся доступным только замыканию и сохраняется между вызовами.
make_memoized_fib <- function() {
cache <- c()
function(n) {
if (!is.null(cache[n])) return(cache[n])
if (n <= 1) { cache[n] <<- n; return(n) }
result <- Recall(n-1) + Recall(n-2)
cache[n] <<- result
result
}
}
fib <- make_memoized_fib()
cat(sapply(0:8, fib), '\n')Особенность лексической области видимости: переменные цикла
Классическая неожиданность возникает при создании замыканий внутри цикла. Все замыкания захватывают одно и то же окружение, а не отдельную копию значения. К моменту вызова в переменной цикла уже находится её последнее значение.
fns <- vector('list', 3)
for (i in 1:3) {
local({
captured_i <- i
fns[[captured_i]] <<- function() captured_i
})
}
cat(fns[[1]](), fns[[2]](), fns[[3]](), '\n')Быстрая проверка
При лексической области видимости в R где функция ищет свободную переменную — переменную, не определённую в самой функции?
Лексическая область видимости: основные выводы
Основные выводы о лексической области видимости:
- В R используется лексическая область видимости — функции ищут переменные там, где они были определены
- Функция захватывает своё объемлющее окружение, образуя замыкание
- Свободные переменные разрешаются во время определения функции, а не вызова
<<-изменяет привязку в цепочке родительских окружений (или в глобальном окружении, если привязка не найдена)- Замыкания +
<<-= функции с состоянием и сохраняемым закрытым состоянием - Особенность замыканий в циклах: используйте
local(), чтобы принудительно создавать новое окружение на каждой итерации
make_multiplier <- function(factor) {
function(x) x * factor
}
double <- make_multiplier(2)
triple <- make_multiplier(3)
cat('double(7):', double(7), '\n')
cat('triple(7):', triple(7), '\n')
cat('double env factor:', get('factor', envir = environment(double)), '\n')Часто задаваемые вопросы
Урок «Правила лексической области видимости» бесплатный?
Да — полный текст урока «Правила лексической области видимости» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс R Academy, подпишись на CoddyKit PRO. Курс R Academy содержит 4 уроков всего.
Чему я научусь в уроке «Правила лексической области видимости»?
Разберитесь, как R находит переменные, выполняя поиск в родительских окружениях. Ты практикуешь R Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать R Academy?
Предыдущий опыт не требуется. R Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «Правила лексической области видимости»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке R Academy?
Да. Каждый урок R Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Что такое окружение в R?
- Правила лексической области видимости
- Глобальная и локальная область видимости
- Создание и проверка окружений