คุณใช้“ << -” (การกำหนดขอบเขต) ใน R อย่างไร


140

ฉันเพิ่งอ่านจบเกี่ยวกับการกำหนดขอบเขตในบทนำ Rและฉันอยากรู้เกี่ยวกับการ<<-มอบหมาย

คู่มือแสดงตัวอย่างหนึ่ง (น่าสนใจมาก) สำหรับ<<-ซึ่งฉันรู้สึกว่าฉันเข้าใจ สิ่งที่ฉันยังขาดอยู่คือบริบทของเมื่อสิ่งนี้มีประโยชน์

ดังนั้นสิ่งที่ฉันชอบที่จะอ่านจากคุณคือตัวอย่าง (หรือลิงก์ไปยังตัวอย่าง) เมื่อการใช้งาน<<-นั้นน่าสนใจ / มีประโยชน์ สิ่งที่อาจเป็นอันตรายจากการใช้มัน (ดูง่ายต่อการติดตามหลวม) และเคล็ดลับใด ๆ ที่คุณอาจรู้สึกอยากแบ่งปัน

คำตอบ:


196

<<-มีประโยชน์มากที่สุดเมื่อใช้ร่วมกับการปิดเพื่อรักษาสถานะ นี่คือหัวข้อจากกระดาษล่าสุดของฉัน:

การปิดเป็นฟังก์ชันที่เขียนโดยฟังก์ชันอื่น การปิดจึงถูกเรียกใช้เนื่องจากมันล้อมรอบสภาพแวดล้อมของฟังก์ชันพาเรนต์และสามารถเข้าถึงตัวแปรและพารามิเตอร์ทั้งหมดในฟังก์ชันนั้นได้ สิ่งนี้มีประโยชน์เพราะช่วยให้เรามีพารามิเตอร์สองระดับ พารามิเตอร์หนึ่งระดับ (พาเรนต์) ควบคุมการทำงานของฟังก์ชัน อีกระดับ (เด็ก) ทำงาน ตัวอย่างต่อไปนี้แสดงวิธีการใช้แนวคิดนี้เพื่อสร้างตระกูลของฟังก์ชันพลังงาน ฟังก์ชั่นหลัก ( power) สร้างฟังก์ชั่นเด็ก ( squareและcube) ที่ทำงานหนักจริง ๆ

power <- function(exponent) {
  function(x) x ^ exponent
}

square <- power(2)
square(2) # -> [1] 4
square(4) # -> [1] 16

cube <- power(3)
cube(2) # -> [1] 8
cube(4) # -> [1] 64

ความสามารถในการจัดการตัวแปรในสองระดับยังทำให้สามารถรักษาสถานะระหว่างการเรียกใช้ฟังก์ชันโดยอนุญาตให้ฟังก์ชันแก้ไขตัวแปรในสภาพแวดล้อมของพาเรนต์ <<-กุญแจสำคัญในการจัดการตัวแปรในระดับที่แตกต่างกันเป็นผู้ประกอบการที่ได้รับมอบหมายลูกศรคู่ ต่างจากการมอบหมายลูกศรเดียวตามปกติ ( <-) ที่ใช้งานได้กับระดับปัจจุบันเสมอตัวดำเนินการลูกศรคู่สามารถแก้ไขตัวแปรในระดับพาเรนต์

สิ่งนี้ทำให้เป็นไปได้ที่จะรักษาตัวนับที่บันทึกจำนวนครั้งที่ฟังก์ชันถูกเรียกใช้ดังตัวอย่างต่อไปนี้แสดง ทุกครั้งที่new_counterมีการเรียกใช้จะสร้างสภาพแวดล้อมเริ่มต้นตัวนับiในสภาพแวดล้อมนี้แล้วสร้างฟังก์ชันใหม่

new_counter <- function() {
  i <- 0
  function() {
    # do something useful, then ...
    i <<- i + 1
    i
  }
}

ฟังก์ชั่นใหม่เป็นการปิดและสภาพแวดล้อมของมันคือสภาพแวดล้อมที่ล้อมรอบ เมื่อการปิดcounter_oneและcounter_twoเรียกใช้แต่ละตัวแก้ไขเคาน์เตอร์ในสภาพแวดล้อมที่ล้อมรอบแล้วส่งกลับจำนวนปัจจุบัน

counter_one <- new_counter()
counter_two <- new_counter()

counter_one() # -> [1] 1
counter_one() # -> [1] 2
counter_two() # -> [1] 1

4
เฮ้นี่เป็นงาน R ที่ยังไม่แก้ใน Rosettacode ( rosettacode.org/wiki/Accumulator_factory#R ) มันเป็น ...
Karsten W.

1
จะต้องมีการปิดมากกว่า 1 ครั้งในฟังก์ชั่นผู้ปกครองคนเดียวหรือไม่? ฉันลองตัวอย่างหนึ่งดูเหมือนว่าเฉพาะการปิดครั้งสุดท้ายเท่านั้นที่ถูกประหาร ...
mckf111

มีทางเลือกที่เท่าเทียมกันกับเครื่องหมาย "<< -" หรือไม่?
Genom

38

ช่วยให้คิดว่า<<-เทียบเท่ากับassign(หากคุณตั้งค่าinheritsพารามิเตอร์ในฟังก์ชันนั้นเป็นTRUE) ประโยชน์ของassignมันคือช่วยให้คุณสามารถระบุพารามิเตอร์เพิ่มเติม (เช่นสภาพแวดล้อม) ดังนั้นฉันชอบที่จะใช้assignมากกว่า<<-ในกรณีส่วนใหญ่

การใช้<<-และassign(x, value, inherits=TRUE)หมายความว่า "สภาพแวดล้อมที่ล้อมรอบของสภาพแวดล้อมที่จัดให้มีการค้นหาจนกว่าตัวแปร 'x' จะพบ" กล่าวอีกนัยหนึ่งมันจะทำการตรวจสอบสภาพแวดล้อมตามลำดับจนกว่าจะพบตัวแปรที่มีชื่อนั้นและจะกำหนดให้กับสิ่งนั้น สิ่งนี้อาจอยู่ในขอบเขตของฟังก์ชันหรือในสภาพแวดล้อมส่วนกลาง

เพื่อให้เข้าใจถึงสิ่งที่ฟังก์ชั่นเหล่านี้ทำคุณต้องเข้าใจสภาพแวดล้อม R (เช่นการใช้search)

ฉันใช้ฟังก์ชั่นเหล่านี้เป็นประจำเมื่อฉันใช้การจำลองขนาดใหญ่และฉันต้องการบันทึกผลลัพธ์ระดับกลาง สิ่งนี้อนุญาตให้คุณสร้างวัตถุที่อยู่นอกขอบเขตของฟังก์ชั่นหรือapplyลูปที่กำหนด มีประโยชน์มากโดยเฉพาะถ้าคุณมีข้อกังวลเกี่ยวกับลูปขนาดใหญ่ที่สิ้นสุดโดยไม่คาดคิด (เช่นการขาดการเชื่อมต่อฐานข้อมูล) ซึ่งในกรณีนี้คุณอาจสูญเสียทุกอย่างในกระบวนการ นี่จะเทียบเท่ากับการเขียนผลลัพธ์ของคุณไปยังฐานข้อมูลหรือไฟล์ในระหว่างกระบวนการที่ใช้เวลานานยกเว้นว่าจะเก็บผลลัพธ์ไว้ในสภาพแวดล้อม R แทน

<<-เตือนหลักของฉันด้วยนี้ต้องระวังเพราะคุณตอนนี้ทำงานกับตัวแปรทั่วโลกโดยเฉพาะอย่างยิ่งเมื่อใช้ นั่นหมายความว่าคุณสามารถจบลงด้วยสถานการณ์ที่ฟังก์ชันใช้ค่าวัตถุจากสภาพแวดล้อมเมื่อคุณคาดว่าจะใช้ค่าที่ให้มาเป็นพารามิเตอร์ นี่คือหนึ่งในสิ่งสำคัญที่การเขียนโปรแกรมใช้งานได้พยายามหลีกเลี่ยง (ดูผลข้างเคียง ) ฉันหลีกเลี่ยงปัญหานี้โดยการกำหนดค่าของฉันให้กับชื่อตัวแปรที่ไม่ซ้ำกัน (โดยใช้การวางด้วยชุดหรือพารามิเตอร์ที่ไม่ซ้ำกัน) ที่ไม่เคยใช้ภายในฟังก์ชั่น แต่ใช้สำหรับแคชและในกรณีที่ฉันต้องการกู้คืนในภายหลัง - การวิเคราะห์ผลกลาง)


3
ขอบคุณทัล ฉันมีบล็อก แต่ฉันไม่ได้ใช้จริงๆ ฉันไม่สามารถเสร็จสิ้นการโพสต์เพราะผมไม่ต้องการที่จะเผยแพร่อะไรจนกว่าจะสมบูรณ์แบบและฉันก็ไม่ได้มีเวลาสำหรับการที่ ...
เชน

2
ปราชญ์พูดกับฉันครั้งหนึ่งว่าไม่สำคัญที่จะต้องสมบูรณ์แบบ - เพียงแค่ยืนออก - ซึ่งคุณเป็นและโพสต์ของคุณจะเป็นเช่นนั้น นอกจากนี้ - บางครั้งผู้อ่านช่วยปรับปรุงข้อความพร้อมความคิดเห็น (นั่นคือสิ่งที่เกิดขึ้นกับบล็อกของฉัน) ฉันหวังว่าวันหนึ่งคุณจะพิจารณา :)
Tal Galili

9

ที่เดียวที่ฉันใช้<<-คือใน GUI อย่างง่ายโดยใช้ tcl / tk ตัวอย่างเริ่มต้นบางตัวอย่างมี - ตามที่คุณต้องการแยกความแตกต่างระหว่างตัวแปรโลคอลและตัวแปรโกลบอลสำหรับสถานะเต็ม ดูตัวอย่าง

 library(tcltk)
 demo(tkdensity)

<<-ซึ่งการใช้งาน มิฉะนั้นฉันเห็นด้วยกับ Marek :) - การค้นหาของ Google สามารถช่วยได้


น่าสนใจฉันไม่สามารถหาได้tkdensityใน R 3.6.0
NelsonGon

1
แพคเกจ tcltk มาพร้อมกับ R: github.com/wch/r-source/blob/trunk/src/library/tcltk/demo/…
Dirk Eddelbuettel

5
f <- function(n, x0) {x <- x0; replicate(n, (function(){x <<- x+rnorm(1)})())}
plot(f(1000,0),typ="l")

11
นี่เป็นตัวอย่างที่ดีว่าไม่ควรใช้<<-ที่ไหน ห่วงสำหรับจะชัดเจนในกรณีนี้
hadley

4

ในเรื่องนี้ฉันต้องการที่จะชี้ให้เห็นว่า<<-ผู้ประกอบการจะทำงานแปลก ๆ เมื่อนำไปใช้ (ไม่ถูกต้อง) ภายในสำหรับวง (อาจมีกรณีอื่นด้วย) รับรหัสต่อไปนี้:

fortest <- function() {
    mySum <- 0
    for (i in c(1, 2, 3)) {
        mySum <<- mySum + i
    }
    mySum
}

คุณอาจคาดหวังว่าฟังก์ชั่นจะคืนค่าผลรวมที่คาดหวัง, 6, แต่มันกลับเป็น 0, ด้วยตัวแปรทั่วโลกที่mySumถูกสร้างขึ้นและกำหนดค่า 3 ฉันไม่สามารถอธิบายสิ่งที่เกิดขึ้นที่นี่ได้อย่างเต็มที่ การวนซ้ำไม่ใช่ 'ระดับ' ขอบเขตใหม่ แต่ดูเหมือนว่า R จะมองภายนอกfortestฟังก์ชั่นไม่พบmySumตัวแปรที่จะกำหนดให้ดังนั้นสร้างหนึ่งและกำหนดค่า 1 ซึ่งเป็นครั้งแรกที่ผ่านลูป ในการวนซ้ำครั้งต่อไป RHS ในการกำหนดจะต้องอ้างถึงmySumตัวแปรภายใน (ไม่เปลี่ยนแปลง) ในขณะที่ LHS อ้างถึงตัวแปรทั่วโลก ดังนั้นการวนซ้ำแต่ละครั้งจะเขียนทับค่าของตัวแปรทั่วโลกเป็นค่าของการวนซ้ำiนั้นดังนั้นจึงมีค่า 3 เมื่อออกจากฟังก์ชัน

หวังว่านี่จะช่วยใครซักคน - นี่ทำให้ฉันงงงวยสองสามชั่วโมงวันนี้! (BTW เพียงแทนที่<<-ด้วย<-และฟังก์ชั่นการทำงานตามที่คาดไว้)


2
ในตัวอย่างของคุณโลคอลนั้นmySumจะไม่เพิ่มขึ้น แต่จะมีเฉพาะโกลบอลmySumเท่านั้น ดังนั้นในการทำซ้ำของห่วงแต่ละทั่วโลกได้รับค่าmySum คุณสามารถทำตามนี้ด้วย0 + i debug(fortest)
ClementWalter

มันไม่มีอะไรเกี่ยวข้องกับการเป็น for-loop คุณกำลังอ้างอิงขอบเขตที่แตกต่างกันสองขอบเขต เพียงใช้<-ทุกที่อย่างสม่ำเสมอในฟังก์ชั่นถ้าคุณเพียงต้องการปรับปรุงตัวแปรท้องถิ่นภายในฟังก์ชั่น
smci

หรือใช้ << - ทุกที่ @smci แม้ว่าจะดีที่สุดเพื่อหลีกเลี่ยงการกลม
สถิติการเรียนรู้ตามตัวอย่าง

3

<<-ผู้ประกอบการยังสามารถเป็นประโยชน์สำหรับการเรียนการอ้างอิงเมื่อเขียนวิธีการอ้างอิง ตัวอย่างเช่น:

myRFclass <- setRefClass(Class = "RF",
                         fields = list(A = "numeric",
                                       B = "numeric",
                                       C = function() A + B))
myRFclass$methods(show = function() cat("A =", A, "B =", B, "C =",C))
myRFclass$methods(changeA = function() A <<- A*B) # note the <<-
obj1 <- myRFclass(A = 2, B = 3)
obj1
# A = 2 B = 3 C = 5
obj1$changeA()
obj1
# A = 6 B = 3 C = 9
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.