R Language
प्रवाह संरचनाओं को नियंत्रित करें
खोज…
टिप्पणियों
किसी कार्य को दोहराने या किसी डोमेन पर कार्यों के सेट के लिए लूप एक प्रवाह नियंत्रण विधि है। लूप के लिए मुख्य संरचना है
for ( [index] in [domain]){
[body]
}
कहाँ पे
-
[index]
एक नाम है जो लूप के प्रत्येक पुनरावृत्ति पर[domain]
का ठीक एक मूल्य लेता है। -
[domain]
मानों का एक वेक्टर है जिस पर पुनरावृति करना है। -
[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
परिणाम एक ही घटना होगी हालांकि हम चरित्र विकल्प (बी) का उपयोग करते हैं।
इष्टतम निर्माण एक लूप के लिए
लूप निर्माण के लिए अच्छे के प्रभाव को स्पष्ट करने के लिए, हम प्रत्येक स्तंभ के चार अलग-अलग तरीकों से गणना करेंगे:
- लूप के लिए एक खराब अनुकूलित का उपयोग करना
- लूप के लिए एक अच्छी तरह से अनुकूलित का उपयोग करना
- फ़ंक्शंस के एक
*apply
परिवार का उपयोग करना -
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
}