खोज…


टिप्पणियों

किसी कार्य को दोहराने या किसी डोमेन पर कार्यों के सेट के लिए लूप एक प्रवाह नियंत्रण विधि है। लूप के लिए मुख्य संरचना है

for ( [index] in [domain]){
  [body]
}

कहाँ पे

  1. [index] एक नाम है जो लूप के प्रत्येक पुनरावृत्ति पर [domain] का ठीक एक मूल्य लेता है।
  2. [domain] मानों का एक वेक्टर है जिस पर पुनरावृति करना है।
  3. [body] प्रत्येक पुनरावृत्ति पर लागू करने के लिए निर्देशों का समूह है।

एक तुच्छ उदाहरण के रूप में, मानों के वेक्टर के संचयी योग को प्राप्त करने के लिए एक लूप के उपयोग पर विचार करें।

x <- 1:4
cumulative_sum <- 0
for (i in x){
  cumulative_sum <- cumulative_sum + x[i]
}
cumulative_sum

लूप्स के लिए संरचना का अनुकूलन

लूप के लिए अवधारणाओं को दोहराने के लिए अवधारणाओं और निष्पादन कार्यों के लिए उपयोगी हो सकता है। यदि सावधानी से निर्माण नहीं किया जाता है, हालांकि, वे फ़ंक्शन के apply परिवार के पसंदीदा उपयोग की तुलना में निष्पादित करने के लिए बहुत धीमा हो सकते हैं। फिर भी, लूप के निर्माण के लिए कुछ तत्व हैं जिन्हें आप लूप निर्माण में शामिल कर सकते हैं। कई मामलों में, लूप के लिए अच्छा निर्माण कम्प्यूटेशनल दक्षता को लागू फ़ंक्शन के बहुत करीब लाएगा।

लूप के लिए एक 'ठीक से निर्मित' कोर संरचना पर बनाता है और इसमें वस्तु की घोषणा करने वाला एक बयान शामिल होता है जो लूप के प्रत्येक पुनरावृत्ति को कैप्चर करेगा। इस ऑब्जेक्ट में एक वर्ग और घोषित लंबाई दोनों होनी चाहिए।

[output] <- [vector_of_length]
for ([index] in [length_safe_domain]){
  [output][index] <- [body]
}

वर्णन करने के लिए, आइए हम संख्यात्मक मान में प्रत्येक मान को वर्गाकार करने के लिए एक लूप लिखें (यह केवल चित्रण के लिए एक तुच्छ उदाहरण है। इस कार्य को पूरा करने का 'सही' तरीका x_squared <- x^2 ) होगा।

x <- 1:100
x_squared <- vector("numeric", length = length(x))
for (i in seq_along(x)){
  x_squared[i] <- x[i]^2
}

फिर से, ध्यान दें कि हमने पहले आउटपुट x_squared लिए एक x_squared , और इसे x के समान लंबाई के साथ वर्ग "संख्यात्मक" दिया। इसके अतिरिक्त, हमने seq_along फ़ंक्शन का उपयोग करके "लंबाई सुरक्षित डोमेन" घोषित किया। seq_along एक वस्तु के लिए सूचकांकों का एक वेक्टर बनाता है जो छोरों के लिए उपयोग के लिए अनुकूल है। हालांकि, यह for (i in 1:length(x)) का उपयोग करने के लिए सहज है, अगर x में 0 लंबाई है, तो लूप 1:0 के डोमेन पर पुनरावृति करने का प्रयास करेगा, जिसके परिणामस्वरूप त्रुटि हो सकती है (0th इंडेक्स R में अपरिभाषित है। )।

रिसेप्टकल ऑब्जेक्ट्स और लंबाई सुरक्षित डोमेन को कार्यों के apply परिवार द्वारा आंतरिक रूप से नियंत्रित किया जाता है और उपयोगकर्ताओं को यथासंभव छोरों के लिए apply दृष्टिकोण को अपनाने के apply प्रोत्साहित किया जाता है। हालांकि, यदि ठीक से निर्माण किया गया है, तो लूप के लिए दक्षता की न्यूनतम हानि के साथ कभी-कभी अधिक कोड स्पष्टता प्रदान की जा सकती है।

लूप्स के लिए वेक्टरिंग

छोरों के लिए अक्सर उन कार्यों की अवधारणा में एक उपयोगी उपकरण हो सकता है जिन्हें प्रत्येक पुनरावृत्ति के भीतर पूरा करने की आवश्यकता होती है। जब लूप पूरी तरह से विकसित और अवधारणा है, तो लूप को एक फ़ंक्शन में बदलने के फायदे हो सकते हैं।

इस उदाहरण में, हम mtcars डेटासेट में प्रत्येक स्तंभ के माध्य की गणना करने के लिए एक लूप के लिए विकसित करेंगे (फिर से, एक तुच्छ उदाहरण क्योंकि इसे colMeans फ़ंक्शन के माध्यम से पूरा किया जा सकता है)।

column_mean_loop <- vector("numeric", length(mtcars))
for (k in seq_along(mtcars)){
  column_mean_loop[k] <- mean(mtcars[[k]])
}

लूप के लिए एक फ़ंक्शन के रूप में लूप के शरीर को फिर से लिखकर एक लागू फ़ंक्शन में परिवर्तित किया जा सकता है।

col_mean_fn <- function(x) mean(x)
column_mean_apply <- vapply(mtcars, col_mean_fn, numeric(1))

और परिणामों की तुलना करने के लिए:

identical(column_mean_loop, 
          unname(column_mean_apply)) #* vapply added names to the elements
                                     #* remove them for comparison

सदिश रूप का लाभ यह है कि हम कोड की कुछ पंक्तियों को समाप्त करने में सक्षम थे। लंबाई और प्रकार के आउटपुट ऑब्जेक्ट को निर्धारित करने और एक लंबाई सुरक्षित डोमेन पर चलने के मैकेनिक्स को हमारे द्वारा लागू फ़ंक्शन द्वारा नियंत्रित किया जाता है। इसके अतिरिक्त, लागू फ़ंक्शन लूप की तुलना में थोड़ा तेज है। पुनरावृत्तियों की संख्या और शरीर की जटिलता के आधार पर मानव के संदर्भ में गति का अंतर अक्सर नगण्य होता है।

बुनियादी लूप निर्माण के लिए

इस उदाहरण में हम डेटा स्तंभ में प्रत्येक स्तंभ के लिए चुकता mtcars की गणना करेंगे, इस मामले में mtcars

विकल्प A: पूर्णांक सूचकांक

squared_deviance <- vector("list", length(mtcars))
for (i in seq_along(mtcars)){
  squared_deviance[[i]] <- (mtcars[[i]] - mean(mtcars[[i]]))^2
}

squared_deviance एक 11 तत्वों की सूची है, जैसा कि अपेक्षित था।

class(squared_deviance)
length(squared_deviance)

विकल्प बी: चरित्र सूचकांक

squared_deviance <- vector("list", length(mtcars))
Squared_deviance <- setNames(squared_deviance, names(mtcars))
for (k in names(mtcars)){
  squared_deviance[[k]] <- (mtcars[[k]] - mean(mtcars[[k]]))^2
}

अगर हम परिणाम के रूप में data.frame चाहते हैं तो क्या होगा? खैर, सूची को अन्य वस्तुओं में बदलने के लिए कई विकल्प हैं। हालाँकि, और हो सकता है कि इस मामले में सबसे सरल, परिणाम के for data.frame को संग्रहीत करना होगा।

squared_deviance <- mtcars #copy the original
squared_deviance[TRUE]<-NA  #replace with NA or do squared_deviance[,]<-NA
for (i in seq_along(mtcars)){
  squared_deviance[[i]] <- (mtcars[[i]] - mean(mtcars[[i]]))^2
}
dim(squared_deviance)
[1] 32 11

परिणाम एक ही घटना होगी हालांकि हम चरित्र विकल्प (बी) का उपयोग करते हैं।

इष्टतम निर्माण एक लूप के लिए

लूप निर्माण के लिए अच्छे के प्रभाव को स्पष्ट करने के लिए, हम प्रत्येक स्तंभ के चार अलग-अलग तरीकों से गणना करेंगे:

  1. लूप के लिए एक खराब अनुकूलित का उपयोग करना
  2. लूप के लिए एक अच्छी तरह से अनुकूलित का उपयोग करना
  3. फ़ंक्शंस के एक *apply परिवार का उपयोग करना
  4. colMeans फ़ंक्शन का उपयोग करना

इन विकल्पों में से प्रत्येक को कोड में दिखाया जाएगा; प्रत्येक विकल्प को निष्पादित करने के लिए कम्प्यूटेशनल समय की तुलना दिखाई जाएगी; और अंत में मतभेदों की एक चर्चा दी जाएगी।

लूप के लिए पूरी तरह से अनुकूलित

column_mean_poor <- NULL
for (i in 1:length(mtcars)){
  column_mean_poor[i] <- mean(mtcars[[i]])
}

लूप के लिए अच्छी तरह से अनुकूलित

column_mean_optimal <- vector("numeric", length(mtcars))
for (i in seq_along(mtcars)){
  column_mean_optimal <- mean(mtcars[[i]])
}

vapply फ़ंक्शन

column_mean_vapply <- vapply(mtcars, mean, numeric(1))

colMeans फ़ंक्शन

column_mean_colMeans <- colMeans(mtcars)

दक्षता तुलना

इन चार तरीकों के बेंचमार्किंग के परिणाम नीचे दिखाए गए हैं (कोड प्रदर्शित नहीं किया गया है)

Unit: microseconds
     expr     min       lq     mean   median       uq     max neval  cld
     poor 240.986 262.0820 287.1125 275.8160 307.2485 442.609   100    d
  optimal 220.313 237.4455 258.8426 247.0735 280.9130 362.469   100   c 
   vapply 107.042 109.7320 124.4715 113.4130 132.6695 202.473   100 a   
 colMeans 155.183 161.6955 180.2067 175.0045 194.2605 259.958   100  b

ध्यान दें कि लूप के for अनुकूलित लूप के लिए खराब तरीके से निर्मित है। लूप के लिए खराब तरीके से निर्मित आउटपुट ऑब्जेक्ट की लंबाई लगातार बढ़ रही है, और लंबाई के प्रत्येक परिवर्तन पर, आर ऑब्जेक्ट के वर्ग का पुनर्मूल्यांकन कर रहा है।

इस ओवरहेड बोझ में से कुछ लूप शुरू करने से पहले आउटपुट ऑब्जेक्ट के प्रकार और इसकी लंबाई को घोषित करके लूप के लिए अनुकूलित द्वारा हटा दिया जाता है।

इस उदाहरण में, हालांकि, एक vapply फ़ंक्शन का उपयोग कम्प्यूटेशनल दक्षता को दोगुना कर देता है, मोटे तौर पर क्योंकि हमने आर को बताया था कि परिणाम संख्यात्मक होना चाहिए (यदि कोई एक परिणाम संख्यात्मक नहीं था, तो एक त्रुटि वापस आ जाएगी)।

उपयोग की colMeans समारोह की तुलना में एक स्पर्श धीमी है vapply कार्य करते हैं। यह अंतर colMeans में की colMeans कुछ त्रुटि जांचों और मुख्य रूप से as.matrix रूपांतरण (क्योंकि mtcars एक data.frame ) के लिए vapply , जो vapply फ़ंक्शन में प्रदर्शन नहीं किए गए थे।

अन्य लूपिंग निर्माण: और फिर से

आर दो अतिरिक्त लूपिंग निर्माण प्रदान करता है, while और repeat , जो आमतौर पर उन परिस्थितियों में उपयोग किया जाता है जहां आवश्यक पुनरावृत्तियों की संख्या अनिश्चित है।


while पाश

एक while लूप का सामान्य रूप निम्नानुसार है,

while (condition) {
    ## do something
    ## in loop body
}

जहां लूप बॉडी में प्रवेश करने से पहले condition का मूल्यांकन किया जाता है। यदि condition TRUE मूल्यांकन करती है, तो लूप बॉडी के अंदर के कोड को निष्पादित किया जाता है, और यह प्रक्रिया तब तक दोहराती है जब तक कि condition FALSE मूल्यांकन नहीं करती (या एक break स्टेटमेंट पहुँच जाता है; नीचे देखें)। विपरीत for पाश, एक अगर while पाश एक चर का उपयोग करता है वृद्धिशील पुनरावृत्तियों प्रदर्शन करने के लिए, चर घोषित की जानी चाहिए और समय से आगे प्रारंभ, और पाश शरीर के भीतर अद्यतन किया जाना चाहिए। उदाहरण के लिए, निम्नलिखित लूप समान कार्य पूरा करते हैं:

for (i in 0:4) {
    cat(i, "\n")
}
# 0 
# 1 
# 2 
# 3 
# 4 

i <- 0
while (i < 5) {
    cat(i, "\n")
    i <- i + 1
}
# 0 
# 1 
# 2 
# 3 
# 4 

में while ऊपर पाश, लाइन i <- i + 1 अनंत लूप को रोकने के लिए आवश्यक है।


इसके अतिरिक्त, लूप बॉडी के अंदर से break लिए कॉल के साथ while लूप को समाप्त करना संभव है:

iter <- 0
while (TRUE) {
    if (runif(1) < 0.25) {
        break
    } else {
        iter <- iter + 1
    }
}
iter
#[1] 4

इस उदाहरण में, condition हमेशा TRUE , इसलिए लूप को समाप्त करने का एकमात्र तरीका शरीर के अंदर break लिए कॉल है। ध्यान दें कि जब यह उदाहरण चलाया जाता है, तो iter का अंतिम मूल्य आपके PRNG की स्थिति पर निर्भर करेगा, और प्रत्येक बार कोड निष्पादित होने पर अलग-अलग परिणाम (अनिवार्य रूप से) का उत्पादन करना चाहिए।


repeat लूप

repeat निर्माण अनिवार्य रूप से उसी while (TRUE) { ## something } , और इसका निम्न रूप है:

repeat ({
    ## do something
    ## in loop body
})

अतिरिक्त {} आवश्यकता नहीं है, लेकिन () हैं। पिछले उदाहरण का repeat का उपयोग करते repeat ,

iter <- 0
repeat ({
    if (runif(1) < 0.25) {
        break
    } else {
        iter <- iter + 1
    }
})
iter
#[1] 2 

break पर अधिक

यह ध्यान रखना महत्वपूर्ण है कि break केवल तुरंत संलग्न लूप को समाप्त करेगा। अर्थात्, एक अनंत लूप है:

while (TRUE) {
    while (TRUE) {
        cat("inner loop\n")
        break
    }
    cat("outer loop\n")
}

थोड़ी रचनात्मकता के साथ, हालांकि, नेस्टेड लूप के भीतर से पूरी तरह से टूटना संभव है। एक उदाहरण के रूप में, निम्नलिखित अभिव्यक्ति पर विचार करें, जो अपनी वर्तमान स्थिति में, अनंत रूप से लूप करेगी:

while (TRUE) {
    cat("outer loop body\n")
    while (TRUE) {
        cat("inner loop body\n")
        x <- runif(1)
        if (x < .3) {
            break
        } else {
            cat(sprintf("x is %.5f\n", x))
        }
    }
}

एक संभावना यह है कि पहचान करने के लिए है, के विपरीत break , return अभिव्यक्ति संलग्न छोरों के अनेक स्तरों पर नियंत्रण वापस जाने के लिए की क्षमता है। हालाँकि, चूंकि return केवल एक फ़ंक्शन के भीतर उपयोग किए जाने पर मान्य होता है, इसलिए हम केवल break को return() साथ प्रतिस्थापित नहीं कर सकते हैं, लेकिन एक अनाम फ़ंक्शन के रूप में संपूर्ण अभिव्यक्ति को लपेटने की आवश्यकता है:

(function() {
    while (TRUE) {
        cat("outer loop body\n")
        while (TRUE) {
            cat("inner loop body\n")
            x <- runif(1)
            if (x < .3) {
                return()
            } else {
                cat(sprintf("x is %.5f\n", x))
            }
        }
    }
})()

वैकल्पिक रूप से, हम अभिव्यक्ति से पहले एक डमी चर ( exit ) बना सकते हैं, और इसे <<- माध्यम से सक्रिय कर सकते हैं <<- जब हम समाप्त करने के लिए तैयार होते हैं तो आंतरिक लूप से:

exit <- FALSE
while (TRUE) {
    cat("outer loop body\n")
    while (TRUE) {
        cat("inner loop body\n")
        x <- runif(1)
        if (x < .3) {
            exit <<- TRUE
            break
        } else {
            cat(sprintf("x is %.5f\n", x))
        }
    }
    if (exit) break
}


Modified text is an extract of the original Stack Overflow Documentation
के तहत लाइसेंस प्राप्त है CC BY-SA 3.0
से संबद्ध नहीं है Stack Overflow