0Pricing
R Academy · บทเรียน

กฎขอบเขตแบบเล็กซิคัล

ทำความเข้าใจว่า R ค้นหาตัวแปรอย่างไรโดยค้นหาใน environment ของ parent

กฎขอบเขตแบบเล็กซิคัล เป็นบทเรียน R Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 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')

คำถามที่พบบ่อย

บทเรียน “กฎขอบเขตแบบเล็กซิคัล” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “กฎขอบเขตแบบเล็กซิคัล” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส R Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส R Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “กฎขอบเขตแบบเล็กซิคัล”

ทำความเข้าใจว่า R ค้นหาตัวแปรอย่างไรโดยค้นหาใน environment ของ parent คุณปฏิบัติ R Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน R Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน R Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน

บทเรียน “กฎขอบเขตแบบเล็กซิคัล” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน R Academy นี้ได้ไหม

ได้ บทเรียน R Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. Environment ใน R คืออะไร
  2. กฎขอบเขตแบบเล็กซิคัล
  3. ขอบเขตส่วนกลางเทียบกับขอบเขตเฉพาะที่
  4. การสร้างและตรวจสอบ Environment
← กลับไปที่ R Academy