0Pricing
R Academy · レッスン

レキシカルスコープの規則

親Environmentを検索してRが変数を見つける仕組みを理解します。

「レキシカルスコープの規則」はCoddyKit上の無料R Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これは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が変数を検索するときは、関数自身の環境 → 包含環境 → 包含環境の包含環境 → … → グローバル環境 → 検索パス → base → empty の順に環境を検索します。最初に見つかったものが使われます。

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時間対応のAIチューター)、R Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 R Academyコースには全4レッスンが含まれています。

「レキシカルスコープの規則」で何を学びますか?

親Environmentを検索してRが変数を見つける仕組みを理解します。 ブラウザで直接実行するハンズオンコードでR Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

R Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのR Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「レキシカルスコープの規則」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このR Academyレッスンでコードを書いて実行できますか?

はい。すべてのR Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. RのEnvironmentとは
  2. レキシカルスコープの規則
  3. グローバルスコープとローカルスコープ
  4. Environmentの作成と確認
← R Academyに戻る