स्पष्ट रूप से एक फ़ंक्शन में रिटर्न कॉल करना या नहीं


199

कुछ समय पहले, मैं आर कोर टीम (साइमन अर्बनक) द्वारा आर कोर टीम (मुझे विश्वास है) returnसे एक फ़ंक्शन के अंत में स्पष्ट रूप से कॉल करने के लिए एक उपयोगकर्ता की सिफारिश करने के लिए फटकार लगाई गई थी (उनकी टिप्पणी हालांकि हटा दी गई थी):

foo = function() {
  return(value)
}

इसके बजाय उन्होंने सिफारिश की:

foo = function() {
  value
}

शायद इस तरह की स्थिति में यह आवश्यक है:

foo = function() {
 if(a) {
   return(a)
 } else {
   return(b)
 }
}

उनकी टिप्पणी ने कुछ प्रकाश डाला कि क्यों नहीं बुला रहे returnजब तक कि सख्त जरूरत एक अच्छी बात नहीं है, लेकिन इसे हटा दिया गया था।

मेरा सवाल है: क्यों returnतेजी से या बेहतर कॉल नहीं कर रहा है, और इस तरह बेहतर है?


12
returnपिछले उदाहरण में भी अनावश्यक है। हटाने returnसे यह थोड़ा तेज़ हो सकता है, लेकिन मेरे विचार में ऐसा इसलिए है क्योंकि R को एक मजेदार प्रोग्रामिंग भाषा कहा जाता है।
१३:०

4
@kohske क्या आप एक उत्तर में अपनी टिप्पणी का विस्तार कर सकते हैं, इस बारे में अधिक विवरण सहित कि यह तेज क्यों है, और यह आर एक कार्यात्मक प्रोग्रामिंग भाषा होने के नाते कैसे संबंधित है?
पॉल हीमस्ट्रा

2
returnगैर-स्थानीय कूद प्रेरित करता है, और एफपी के लिए स्पष्ट गैर-स्थानीय कूद असामान्य है। दरअसल, उदाहरण के लिए, स्कीम नहीं है return। मुझे लगता है कि मेरी टिप्पणियाँ एक उत्तर के रूप में बहुत छोटी (और शायद गलत हैं)।
kohske

2
एफ # नहीं है return, break, continueजो थकाऊ कभी कभी या तो।
कॉलिनफैंग

जवाबों:


128

सवाल था: क्यों नहीं (स्पष्ट रूप से) कॉलिंग तेजी से या बेहतर है, और इस तरह बेहतर है?

इस तरह की धारणा बनाने वाले आर प्रलेखन में कोई कथन नहीं है।
मुख्य पृष्ठ? 'फ़ंक्शन' कहता है:

function( arglist ) expr
return(value)

क्या बिना रिटर्न के कॉल करना तेज है?

दोनों function()और return()आदिम कार्य हैं और function()खुद को पिछले का मूल्यांकन मान देता है भी शामिल किए बिना return()कार्य करते हैं।

कॉलिंग return()के रूप में .Primitive('return')एक तर्क के रूप कि पिछले मूल्य के साथ ही काम करते हैं, लेकिन एक कॉल अधिक की जरूरत होगी। ताकि यह (अक्सर) अनावश्यक .Primitive('return')कॉल अतिरिक्त संसाधनों को आकर्षित कर सके। सरल माप हालांकि यह दर्शाता है कि परिणामी अंतर बहुत छोटा है और इस प्रकार स्पष्ट रिटर्न का उपयोग नहीं करने का कारण नहीं हो सकता है। निम्न प्लॉट को इस तरह से चयनित डेटा से बनाया गया है:

bench_nor2 <- function(x,repeats) { system.time(rep(
# without explicit return
(function(x) vector(length=x,mode="numeric"))(x)
,repeats)) }

bench_ret2 <- function(x,repeats) { system.time(rep(
# with explicit return
(function(x) return(vector(length=x,mode="numeric")))(x)
,repeats)) }

maxlen <- 1000
reps <- 10000
along <- seq(from=1,to=maxlen,by=5)
ret <- sapply(along,FUN=bench_ret2,repeats=reps)
nor <- sapply(along,FUN=bench_nor2,repeats=reps)
res <- data.frame(N=along,ELAPSED_RET=ret["elapsed",],ELAPSED_NOR=nor["elapsed",])

# res object is then visualized
# R version 2.15

समारोह की तुलना में समय बीत गया

ऊपर दी गई तस्वीर आपके प्लेटफ़ॉर्म पर थोड़ी भिन्न हो सकती है। मापा डेटा के आधार पर, लौटी हुई वस्तु के आकार में कोई अंतर नहीं होता है, रिपीट की संख्या (भले ही स्केल की गई हो) केवल एक बहुत छोटा अंतर बनाती है, जो वास्तविक शब्द में वास्तविक डेटा और वास्तविक एल्गोरिथ्म को गिना नहीं जा सकता है या आपका नहीं बना सकता है स्क्रिप्ट तेजी से चलती है।

क्या रिटर्न के बिना कॉल करना बेहतर है?

Return कोड के स्पष्ट रूप से "पत्ते" को डिजाइन करने के लिए अच्छा उपकरण है जहां दिनचर्या समाप्त होनी चाहिए, फ़ंक्शन से बाहर निकलें और मूल्य वापस करें।

# here without calling .Primitive('return')
> (function() {10;20;30;40})()
[1] 40
# here with .Primitive('return')
> (function() {10;20;30;40;return(40)})()
[1] 40
# here return terminates flow
> (function() {10;20;return();30;40})()
NULL
> (function() {10;20;return(25);30;40})()
[1] 25
> 

यह प्रोग्रामर की रणनीति और प्रोग्रामिंग शैली पर निर्भर करता है कि वह किस शैली का उपयोग करता है, वह कोई रिटर्न () का उपयोग नहीं कर सकता है क्योंकि इसकी आवश्यकता नहीं है।

कोर प्रोग्रामर दोनों दृष्टिकोणों अर्थात् का उपयोग करता है। स्पष्ट विवरण के साथ और बिना (functions आधार ’) के स्रोतों में खोजना संभव है।

कई बार केवल लौटाया जाता है () का उपयोग किया जाता है (कोई तर्क नहीं) पूर्ण रूप से फ़ंक्शन को रोकने के लिए मामलों में NULL लौटाना।

यह स्पष्ट नहीं है कि यह बेहतर है या नहीं जैसा कि मानक उपयोगकर्ता या आर का उपयोग करने वाले विश्लेषक वास्तविक अंतर नहीं देख सकते हैं।

मेरी राय है कि सवाल यह होना चाहिए: क्या आर कार्यान्वयन से आने वाले स्पष्ट रिटर्न का उपयोग करने में कोई खतरा है?

या, शायद बेहतर, उपयोगकर्ता लेखन फ़ंक्शन कोड को हमेशा पूछना चाहिए: फ़ंक्शन कोड में स्पष्ट रिटर्न (या कोड शाखा के अंतिम पत्ते के रूप में लौटाए जाने वाले ऑब्जेक्ट) का उपयोग नहीं करने में क्या प्रभाव है ?


4
बहुत अच्छे उत्तर के लिए धन्यवाद। मेरा मानना ​​है कि उपयोग करने में कोई खतरा नहीं है return, और यह प्रोग्रामर की प्राथमिकता पर आता है कि इसका उपयोग करना है या नहीं।
पॉल हैमस्ट्रा

37
की गति returnवास्तव में आखिरी चीज है जिसके बारे में आपको चिंता होनी चाहिए।
हेली

2
मुझे लगता है कि यह एक बुरा जवाब है। कारण अनावश्यक returnफ़ंक्शन कॉल का उपयोग करने के मूल्य के एक मूल असहमति के लिए नीचे आते हैं। जो सवाल आपको पूछना चाहिए वह वह नहीं है जिसे आप अंत में प्रस्तावित करते हैं। इसके बजाय, यह है: “ मुझे निरर्थक का उपयोग क्यों करना चाहिएreturn ? कौन सा लाभ प्रदान करता है? जैसा कि यह पता चला है, जवाब "ज्यादा नहीं है", या यहां तक ​​कि "कोई भी नहीं"। आपका उत्तर इसकी सराहना करने में विफल रहता है।
कोनराड रुडोल्फ

@KonradRudolph ... आपने वास्तव में वही दोहराया जो पॉल मूल रूप से पूछ रहे थे (स्पष्ट वापसी क्यों खराब है)। और मैं सही (किसी के लिए भी सही) उत्तर जानना चाहूंगा :)। क्या आप इस साइट के उपयोगकर्ताओं के लिए अपना स्पष्टीकरण प्रदान करना चाहते हैं?
पेट्र मातुसु

1
@Dason मैंने कहीं और यह बताते हुए एक Reddit पोस्ट जोड़ा है कि इस संदर्भ में यह तर्क क्यों त्रुटिपूर्ण है। दुर्भाग्य से टिप्पणी हर बार स्वचालित रूप से हटा दी जाती है। लघु संस्करण वह returnहै जो एक स्पष्ट टिप्पणी की तरह है जो कहता है "इंक्रीमेंट x 1 से", कोड कर के एक टुकड़े के बगल में है x = x + 2। दूसरे शब्दों में, इसका अन्वेषण गवाह है (क) पूरी तरह अप्रासंगिक है, और (ख) यह गलत जानकारी देता है। क्योंकि returnआर में शब्दार्थ हैं, विशुद्ध रूप से, "इस कार्य को निरस्त करें"। इसका अन्य भाषाओं की तरह ही मतलब नहीं है return
कोनराड रुडोल्फ

102

अगर हर कोई इससे सहमत है

  1. return एक समारोह के शरीर के अंत में आवश्यक नहीं है
  2. उपयोग नहीं करना returnथोड़ा तेज है (@ एलन के परीक्षण के अनुसार, 4.3 माइक्रोसेकंड बनाम 5.1)

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

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

पॉल ने इस उदाहरण का उपयोग किया:

foo = function() {
 if(a) {
   return(a)
 } else {
   return(b)
 }
}

दुर्भाग्य से, कोई यह बता सकता है कि इसे आसानी से लिखा जा सकता है:

foo = function() {
 if(a) {
   output <- a
 } else {
   output <- b
 }
output
}

बाद का संस्करण भी कुछ प्रोग्रामिंग कोडिंग मानकों के अनुरूप है जो प्रति फ़ंक्शन एक रिटर्न स्टेटमेंट की वकालत करता है। मुझे लगता है कि एक बेहतर उदाहरण हो सकता है:

bar <- function() {
   while (a) {
      do_stuff
      for (b) {
         do_stuff
         if (c) return(1)
         for (d) {
            do_stuff
            if (e) return(2)
         }
      }
   }
   return(3)
}

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

मुझे नहीं लगता कि गति तर्क एक मान्य है। 0.8 माइक्रोसेकंड अंतर कुछ भी नहीं है जब आप उन कार्यों को देखना शुरू करते हैं जो वास्तव में कुछ करते हैं। आखिरी चीज जो मैं देख सकता हूं वह यह है कि यह कम टाइपिंग है लेकिन हे, मैं आलसी नहीं हूं।


7
+1, returnकुछ मामलों में कथन की स्पष्ट आवश्यकता है , जैसा कि @flodel ने दिखाया है। वैकल्पिक रूप से, ऐसी परिस्थितियां होती हैं, जहां एक वापसी विवरण सबसे अच्छा छोड़ा जाता है, जैसे बहुत सारे और बहुत से छोटे फ़ंक्शन कॉल। अन्य सभी मामलों में, 95% का कहना है कि यह वास्तव में कोई फर्क नहीं पड़ता कि कोई उपयोग करता है returnया नहीं, और यह वरीयता के लिए नीचे आता है। मुझे रिटर्न का उपयोग करना पसंद है क्योंकि यह आपके मतलब में अधिक स्पष्ट है, इस प्रकार अधिक पठनीय है। शायद यह चर्चा <-बनाम के समान है =?
पॉल हीमस्ट्रा

7
यह आर को एक अनिवार्य प्रोग्रामिंग भाषा के रूप में मान रहा है, जो यह नहीं है: यह एक कार्यात्मक प्रोग्रामिंग भाषा है। फ़ंक्शनल प्रोग्रामिंग बस अलग तरह से काम करती है, और returnमान वापस करने के लिए उपयोग करना निरर्थक है, if (x == TRUE)बजाय लिखने के साथ if (x)
कोनराड रुडोल्फ

4
तुम भी फिर से लिखने fooके रूप में foo <- function(x) if (a) a else b(आवश्यक के रूप में लाइनब्रेक के साथ)। स्पष्ट वापसी या मध्यवर्ती मूल्य की कोई आवश्यकता नहीं है।
हेली

26

यह एक दिलचस्प चर्चा है। मुझे लगता है कि @ फ्लोडेल का उदाहरण उत्कृष्ट है। हालाँकि, मुझे लगता है कि यह मेरी बात को दर्शाता है (और @koshke इस पर टिप्पणी में उल्लेख करता है) जो returnसमझ में आता है जब आप एक कार्यात्मक कोडिंग शैली के बजाय एक अनिवार्य का उपयोग करते हैं

इस बिंदु पर विश्वास करने के लिए नहीं, लेकिन मैंने fooइस तरह से फिर से लिखा होगा :

foo = function() ifelse(a,a,b)

एक कार्यात्मक शैली राज्य परिवर्तनों से बचती है, जैसे कि मूल्य का भंडारण output। इस शैली में, returnजगह से बाहर है; fooगणितीय फ़ंक्शन की तरह दिखता है।

मैं @flodel से सहमत हूं: बूलियन चर के एक जटिल प्रणाली का उपयोग barकरना कम स्पष्ट होगा, और आपके पास होने पर व्यर्थ होगा return। क्या बनाता है barताकि उत्तरदायी returnबयान है कि यह एक जरूरी शैली में लिखा जाता है। दरअसल, बूलियन चर एक कार्यात्मक शैली में बचाए गए "राज्य" परिवर्तनों का प्रतिनिधित्व करते हैं।

barकार्यात्मक शैली में फिर से लिखना मुश्किल है , क्योंकि यह सिर्फ छद्म कोड है, लेकिन विचार कुछ इस तरह है:

e_func <- function() do_stuff
d_func <- function() ifelse(any(sapply(seq(d),e_func)),2,3)
b_func <- function() {
  do_stuff
  ifelse(c,1,sapply(seq(b),d_func))
}

bar <- function () {
   do_stuff
   sapply(seq(a),b_func) # Not exactly correct, but illustrates the idea.
}

whileपाश, सबसे कठिन पुनर्लेखन के लिए हो सकता है, क्योंकि यह करने के लिए राज्य में परिवर्तन द्वारा नियंत्रित किया जाता हैं a

कॉल के कारण होने वाली गति हानि returnनगण्य है, लेकिन returnकार्यात्मक शैली में बचने और पुन: लिखने से प्राप्त दक्षता अक्सर भारी होती है। नए उपयोगकर्ताओं को यह बताने से रोकने के लिए कि returnशायद कोई मदद नहीं करेगा, लेकिन एक कार्यात्मक शैली के लिए उन्हें मार्गदर्शन देना होगा।


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

पायथन में, फ़ंक्शन को एक returnबयान की आवश्यकता होती है । हालांकि, यदि आप अपने कार्य को एक कार्यात्मक शैली में क्रमादेशित करते हैं, तो आपके पास returnअपने फ़ंक्शन के अंत में केवल एक बयान होगा ।

एक और StackOverflow पोस्ट से एक उदाहरण का उपयोग करते हुए, हम कहते हैं कि हम एक फ़ंक्शन चाहते थे जो TRUEकिसी दिए गए सभी मानों में एक xअजीब लंबाई थी। हम दो शैलियों का उपयोग कर सकते हैं:

# Procedural / Imperative
allOdd = function(x) {
  for (i in x) if (length(i) %% 2 == 0) return (FALSE)
  return (TRUE)
}

# Functional
allOdd = function(x) 
  all(length(x) %% 2 == 1)

एक कार्यात्मक शैली में, स्वाभाविक रूप से लौटाया जाने वाला मान फ़ंक्शन के सिरों पर आता है। दोबारा, यह एक गणितीय कार्य की तरह दिखता है।

@GSee में उल्लिखित चेतावनियाँ ?ifelseनिश्चित रूप से दिलचस्प हैं, लेकिन मुझे नहीं लगता कि वे समारोह के उपयोग को रोकने की कोशिश कर रहे हैं। वास्तव में, ifelseस्वचालित रूप से वेक्टरिंग कार्यों का लाभ है। उदाहरण के लिए, थोड़ा संशोधित संस्करण पर विचार करें foo:

foo = function(a) { # Note that it now has an argument
 if(a) {
   return(a)
 } else {
   return(b)
 }
}

यह फ़ंक्शन ठीक काम करता है जब length(a)1. होता है, लेकिन यदि आप fooएक के साथ फिर से लिखते हैंifelse

foo = function (a) ifelse(a,a,b)

अब fooकिसी भी लम्बाई पर काम करता है a। वास्तव में, यह तब भी काम करेगा जब aमैट्रिक्स है। एक मूल्य को उसी आकार में वापस करना जैसा testकि एक विशेषता है जो वैश्वीकरण में मदद करता है, समस्या नहीं।


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

इस स्थिति में, उपयोग ifelse(a,a,b)मेरा एक पालतू जानवर है। ऐसा लगता है कि हर पंक्ति ?ifelseचिल्ला रही है, "इसके बजाय मुझे उपयोग न करें if (a) {a} else b।" उदाहरण के लिए "... समान आकार के साथ एक मान देता है test", "अगर yesया noबहुत कम हैं, तो उनके तत्वों को पुनर्नवीनीकरण किया जाता है।", "परिणाम का मोड मूल्य के आधार पर निर्भर कर सकता है test", "परिणाम का वर्ग गुण से लिया गया है testऔर से चयनित मूल्यों के लिए अनुपयुक्त हो सकता है yesऔर no"
GSee

दूसरे नज़र में, fooबहुत मतलब नहीं है; यह हमेशा सच या वापस आ जाएगी bifelseइसका उपयोग करने पर 1 या कई TRUE वापस आएंगे, और / या 1 या कई bएस। प्रारंभ में, मुझे लगा कि फ़ंक्शन का इरादा यह कहना था "यदि कुछ कथन TRUE है, तो कुछ लौटाएँ, अन्यथा, कुछ और लौटाएँ।" मुझे नहीं लगता कि इसे सदिश किया जाना चाहिए, क्योंकि तब यह "TRUE वाले कुछ ऑब्जेक्ट के तत्वों को लौटाएगा, और TRUE नहीं होने वाले सभी तत्वों के लिए, वापसी होगी b
18

22

ऐसा लगता है कि इसके बिना return()यह तेज है ...

library(rbenchmark)
x <- 1
foo <- function(value) {
  return(value)
}
fuu <- function(value) {
  value
}
benchmark(foo(x),fuu(x),replications=1e7)
    test replications elapsed relative user.self sys.self user.child sys.child
1 foo(x)     10000000   51.36 1.185322     51.11     0.11          0         0
2 fuu(x)     10000000   43.33 1.000000     42.97     0.05          0         0

____ EDIT__ _ __ _ __ _ __ _ __ _ ___

मैं अन्य बेंचमार्क ( benchmark(fuu(x),foo(x),replications=1e7)) पर आगे बढ़ता हूं और परिणाम उलटा होता है ... मैं एक सर्वर पर कोशिश करूंगा।


क्या आप इस कारण से टिप्पणी कर सकते हैं कि यह अंतर क्यों होता है?
पॉल हैमस्ट्रा

4
@PaulHiemstra पेट्र का उत्तर इसके प्रमुख कारणों में से एक है; उपयोग करते समय दो कॉल return(), एक यदि आप नहीं करते हैं। यह किसी फ़ंक्शन के अंत में पूरी तरह से बेमानी है क्योंकि function()यह अंतिम मूल्य है। आप केवल एक फ़ंक्शन के कई दोहराव में इसे नोटिस करते हैं, जहां आंतरिक रूप से बहुत कुछ नहीं किया जाता है ताकि return()फ़ंक्शन की कुल गणना समय का बड़ा हिस्सा बन जाए।
गेविन सिम्पसन

13

अंत में स्पष्ट रूप से 'रिटर्न' नहीं डालने के साथ एक समस्या यह है कि यदि कोई विधि के अंत में अतिरिक्त कथन जोड़ता है, तो अचानक रिटर्न मान गलत है:

foo <- function() {
    dosomething()
}

का मान लौटाता है dosomething()

अब हम अगले दिन साथ आते हैं और एक नई लाइन जोड़ते हैं:

foo <- function() {
    dosomething()
    dosomething2()
}

हम चाहते थे कि हमारा कोड मान लौटाए dosomething(), लेकिन इसके बजाय अब ऐसा नहीं है।

एक स्पष्ट वापसी के साथ, यह वास्तव में स्पष्ट हो जाता है:

foo <- function() {
    return( dosomething() )
    dosomething2()
}

हम देख सकते हैं कि इस कोड के बारे में कुछ अजीब है, और इसे ठीक करें:

foo <- function() {
    dosomething2()
    return( dosomething() )
}

1
हां, वास्तव में मुझे लगता है कि डिबगिंग के समय एक स्पष्ट वापसी () उपयोगी है; एक बार कोड साफ़ हो जाने के बाद, इसकी आवश्यकता कम सम्मोहक होती है और मैं इसे न करने की लालित्य पसंद करता हूँ ...
पैट्रिकटी

लेकिन यह वास्तव में वास्तविक कोड में एक समस्या नहीं है, यह विशुद्ध रूप से सैद्धांतिक है। कोड जो इससे पीड़ित हो सकते हैं, उनमें बहुत बड़ी समस्या है: एक अस्पष्ट, भंगुर कोड प्रवाह जो इतना गैर-स्पष्ट है कि साधारण व्यसनी इसे तोड़ देता है।
कोनराड रुडोल्फ

@KonradRudolph मुझे लगता है कि आप इस पर एक नो-ट्रू स्कॉट्समैन का काम कर रहे हैं ;-) "अगर आपके कोड में यह समस्या है, तो आप एक खराब प्रोग्रामर हैं!"। मैं सच में सहमत नहीं हूँ। मुझे लगता है कि whilst आप कोड के छोटे टुकड़ों पर शॉर्ट-कट लेने से दूर हो सकते हैं, जहां आप हर पंक्ति को दिल से जानते हैं, यह आपको काटने के लिए वापस आ जाएगा क्योंकि आपका कोड बड़ा हो जाता है।
ह्यू परकिंस

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

ठीक है ... मैं यह नहीं देखता कि यह आपके returnकथन के बाद आपको कुछ जोड़ने से कैसे रोकता है और यह ध्यान नहीं देता है कि इसे निष्पादित नहीं किया जाएगा। आप बस एक टिप्पणी जोड़ सकते हैं मान के बाद आप वापस लौटना चाहते हैं जैसेdosomething() # this is my return value, don't add anything after it unless you know goddam well what you are doing
lebatsnok

10

मेरा सवाल है: क्यों returnतेजी से फोन नहीं कर रहा है

यह तेजी से है क्योंकि returnआर में एक (आदिम) फ़ंक्शन है, जिसका अर्थ है कि कोड में इसका उपयोग फ़ंक्शन कॉल की लागत को बढ़ाता है। इसकी तुलना अधिकांश अन्य प्रोग्रामिंग भाषाओं में करें, जहां returnएक कीवर्ड है, लेकिन फ़ंक्शन कॉल नहीं है: यह किसी भी रनटाइम कोड निष्पादन के लिए अनुवाद नहीं करता है।

उस ने कहा, इस तरह से एक आदिम कार्य कॉलिंग आर में बहुत तेज है, और returnएक छोटी सी ओवरहेड को बुलाता है । यह छोड़ने का तर्क नहीं है return

या बेहतर, और इस तरह बेहतर है?

क्योंकि इसका उपयोग करने का कोई कारण नहीं है।

क्योंकि यह बेमानी है, और यह उपयोगी अतिरेक नहीं जोड़ता है

स्पष्ट है: अतिरेक कर सकते हैं कभी कभी उपयोगी हो । लेकिन अधिकांश अतिरेक इस तरह का नहीं है। इसके बजाय, यह उस तरह का है जो जानकारी को जोड़ने के बिना दृश्य अव्यवस्था जोड़ता है : यह एक भराव शब्द या चार्टजंक के प्रोग्रामिंग समकक्ष है )।

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

# Add one to the result
result = x + 1

returnआर में उपयोग करना एक ही श्रेणी में आता है, क्योंकि आर एक कार्यात्मक प्रोग्रामिंग भाषा है , और आर में प्रत्येक फ़ंक्शन कॉल का एक मूल्य है । यह R की एक मौलिक संपत्ति है। और एक बार जब आप R कोड को इस नजरिए से देखते हैं कि हर अभिव्यक्ति (प्रत्येक फ़ंक्शन कॉल सहित) का एक मूल्य है, तो सवाल यह हो जाता है: " मुझे क्यों उपयोग करना चाहिएreturn ?" एक सकारात्मक कारण होना चाहिए, क्योंकि डिफ़ॉल्ट का उपयोग नहीं करना है।

इस तरह के एक सकारात्मक कारण एक समारोह से जल्दी बाहर निकलने का संकेत है, एक गार्ड क्लॉज में कहें :

f = function (a, b) {
    if (! precondition(a)) return() # same as `return(NULL)`!
    calculation(b)
}

यह एक गैर-निरर्थक उपयोग है return। हालांकि, अन्य भाषाओं की तुलना में आर में ऐसे गार्ड क्लॉस दुर्लभ हैं, और चूंकि प्रत्येक अभिव्यक्ति का एक मूल्य है, एक नियमित की ifआवश्यकता नहीं है return:

sign = function (num) {
    if (num > 0) {
        1
    } else if (num < 0) {
        -1
    } else {
        0
    }
}

हम fइस तरह से फिर से लिख सकते हैं :

f = function (a, b) {
    if (precondition(a)) calculation(b)
}

... जहां if (cond) exprके रूप में ही है if (cond) expr else NULL

अंत में, मैं तीन सामान्य आपत्तियों को बताना चाहूंगा:

  1. कुछ लोग तर्क देते हैं कि returnस्पष्टता का उपयोग करना , क्योंकि यह संकेत देता है कि "यह फ़ंक्शन एक मान लौटाता है"। लेकिन जैसा कि ऊपर बताया गया है, हर फ़ंक्शन R में कुछ देता है। returnमान वापस करने का एक मार्कर के रूप में सोचना सिर्फ बेमानी नहीं है, यह सक्रिय रूप से गलत है

  2. संबंधित, पायथन के ज़ेन में एक अद्भुत दिशानिर्देश है जिसका हमेशा पालन किया जाना चाहिए:

    निहितार्थ की तुलना में स्पष्ट है।

    कैसे निरर्थक छोड़ने का returnउल्लंघन नहीं करता है? क्योंकि कार्यात्मक भाषा में किसी फ़ंक्शन का रिटर्न मान हमेशा स्पष्ट होता है: यह इसकी अंतिम अभिव्यक्ति है। यह फिर से एक ही गवाह बनाम अतिरेक के बारे में तर्क है ।

    वास्तव में, यदि आप खोजकर्ता चाहते हैं, तो इसका उपयोग नियम के अपवाद को उजागर करने के लिए करें: उन कार्यों को चिह्नित करें जो एक सार्थक मूल्य नहीं लौटाते हैं, जो केवल उनके दुष्प्रभावों (जैसे cat) के लिए कहा जाता है । सिवाय आर के पास returnइस मामले के लिए एक बेहतर मार्कर है invisible:। उदाहरण के लिए, मैं लिखूंगा

    save_results = function (results, file) {
        # … code that writes the results to a file …
        invisible()
    }
  3. लेकिन लंबे कार्यों के बारे में क्या? क्या जो लौटाया जा रहा है उसका ट्रैक खोना आसान नहीं होगा?

    दो उत्तर: पहला, वास्तव में नहीं। नियम स्पष्ट है: किसी फ़ंक्शन की अंतिम अभिव्यक्ति इसका मूल्य है। नज़र रखने के लिए कुछ भी नहीं है।

    लेकिन इससे भी महत्वपूर्ण बात, लंबे कार्यों में समस्या स्पष्ट returnमार्करों की कमी नहीं है । यह फ़ंक्शन की लंबाई है । लंबे कार्य लगभग (?) हमेशा एकल जिम्मेदारी सिद्धांत का उल्लंघन करते हैं और यहां तक ​​कि जब वे नहीं करते हैं तो उन्हें पठनीयता के अलावा टूटने से लाभ होगा।


शायद मुझे यह जोड़ना चाहिए कि कुछ लोग returnइसे अन्य भाषाओं के समान बनाने के लिए उपयोग करने की वकालत करते हैं। लेकिन यह एक बुरा तर्क है: अन्य कार्यात्मक प्रोग्रामिंग भाषाएं returnया तो उपयोग नहीं करती हैं। यह केवल अनिवार्य भाषाएं हैं, जहां हर अभिव्यक्ति का मूल्य नहीं है, जो इसका उपयोग करते हैं।
कोनराड रुडोल्फ

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

6

मैं returnएक चाल के रूप में लगता है । एक सामान्य नियम के रूप में, एक फ़ंक्शन में मूल्यांकन किए गए अंतिम अभिव्यक्ति का मान फ़ंक्शन का मान बन जाता है - और यह सामान्य पैटर्न कई स्थानों पर पाया जाता है। निम्न में से सभी का मूल्यांकन 3:

local({
1
2
3
})

eval(expression({
1
2
3
}))

(function() {
1
2
3
})()

क्या returnवास्तव में एक मूल्य वापस नहीं कर रहा है (यह इसके साथ या इसके बिना किया जाता है) लेकिन अनियमित तरीके से फ़ंक्शन का "ब्रेकिंग आउट"। उस अर्थ में, यह R में GOTO कथन का निकटतम समतुल्य है (इसमें विराम भी हैं और अगले भी)। मैं returnएक फ़ंक्शन के अंत में बहुत कम और कभी भी उपयोग नहीं करता हूं ।

 if(a) {
   return(a)
 } else {
   return(b)
 }

... इसे फिर से लिखा जा सकता है if(a) a else bजो बहुत बेहतर पठनीय और कम घुंघराले-कोष्ठक है। यहाँ कोई ज़रूरत नहीं returnहै। "वापसी" के उपयोग का मेरा प्रोटोटाइपिक मामला कुछ इस तरह होगा ...

ugly <- function(species, x, y){
   if(length(species)>1) stop("First argument is too long.")
   if(species=="Mickey Mouse") return("You're kidding!")
   ### do some calculations 
   if(grepl("mouse", species)) {
      ## do some more calculations
      if(species=="Dormouse") return(paste0("You're sleeping until", x+y))
      ## do some more calculations
      return(paste0("You're a mouse and will be eating for ", x^y, " more minutes."))
      }
   ## some more ugly conditions
   # ...
   ### finally
   return("The end")
   }

आम तौर पर, कई रिटर्न की आवश्यकता बताती है कि समस्या या तो बदसूरत है या बुरी तरह से संरचित है

<>

return काम करने के लिए वास्तव में एक फ़ंक्शन की आवश्यकता नहीं होती है: आप इसका मूल्यांकन करने के लिए अभिव्यक्तियों के सेट से बाहर तोड़ने के लिए उपयोग कर सकते हैं।

getout <- TRUE 
# if getout==TRUE then the value of EXP, LOC, and FUN will be "OUTTA HERE"
# .... if getout==FALSE then it will be `3` for all these variables    

EXP <- eval(expression({
   1
   2
   if(getout) return("OUTTA HERE")
   3
   }))

LOC <- local({
   1
   2
   if(getout) return("OUTTA HERE")
   3
   })

FUN <- (function(){
   1
   2
   if(getout) return("OUTTA HERE")
   3
   })()

identical(EXP,LOC)
identical(EXP,FUN)

आज मुझे एक ऐसा मामला मिला जहां किसी को वास्तव में आवश्यकता हो सकती है return(ऊपर का मेरा बदसूरत उदाहरण अत्यधिक कृत्रिम है): मान लीजिए कि आपको परीक्षण करने की आवश्यकता है कि क्या मूल्य है NULLया NA: इन मामलों में, एक खाली स्ट्रिंग लौटाएं, अन्यथा characterमूल्य वापस करें । लेकिन एक is.na(NULL)त्रुटि का एक परीक्षण देता है, इसलिए ऐसा लगता है कि यह केवल साथ किया जा सकता है if(is.null(x)) return("")और फिर जारी रहेगा if(is.na(x)) .....। (एक के length(x)==0बजाय का उपयोग कर सकते हैं is.null(x), लेकिन अभी भी, length(x)==0 | is.na(x)अगर xयह उपयोग करने के लिए संभव नहीं है NULL।)
lebatsnok

1
ऐसा इसलिए है क्योंकि आपने उपयोग किया |(वेक्टराइज्ड या जहां दोनों पक्षों का मूल्यांकन किया गया है) ||(शॉर्ट सर्किट या, वेक्टराइज्ड नहीं है, जहां विधेयकों का बारी-बारी से मूल्यांकन किया जाता है)। पर विचार करें if (TRUE | stop()) print(1)बनामif (TRUE || stop()) print(1)
ASAC

2

return कोड पठनीयता बढ़ा सकते हैं:

foo <- function() {
    if (a) return(a)       
    b     
}

3
शायद यह हो सकता है, उस पर। लेकिन यह आपके उदाहरण में ऐसा नहीं करता है। इसके बजाय यह कोड प्रवाह को अस्पष्ट (या बल्कि, जटिल) करता है।
कोनराड रुडोल्फ

1
आपके कार्य को सरल बनाया जा सकता है: foo <- function() a || b(जो IMO अधिक पठनीय है; किसी भी मामले में, किसी की राय में कोई "शुद्ध" पठनीयता नहीं है लेकिन पठनीयता है: ऐसे लोग हैं जो कहते हैं कि विधानसभा की भाषा पूरी तरह से पठनीय है)
lebatsnok

1

अतिरेक का तर्क यहाँ बहुत सामने आया है। मेरी राय में छोड़ कारण पर्याप्त नहीं है कि return()। अतिरेक स्वचालित रूप से एक बुरी चीज नहीं है। जब रणनीतिक रूप से उपयोग किया जाता है, तो अतिरेक कोड को स्पष्ट और अधिक मुख्य बनाता है।

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

कॉल करने के लिए एक नगण्य प्रदर्शन के दंड के साथ return()(दूसरों द्वारा यहां पोस्ट किए गए बेंचमार्क के अनुसार) यह सही और गलत के बजाय शैली के लिए नीचे आता है। कुछ के लिए "गलत" होने के लिए, एक स्पष्ट नुकसान होने की आवश्यकता है, और यहां किसी ने भी संतोषजनक ढंग से प्रदर्शित नहीं किया है कि सहित या छोड़ना return()एक सुसंगत नुकसान है। यह बहुत मामला-विशिष्ट और उपयोगकर्ता-विशिष्ट लगता है।

तो यहाँ मैं इस पर खड़ा हूँ।

function(){
  #do stuff
  ...
  abcd
}

मैं "अनाथ" चर के साथ असहज हूं, जैसे ऊपर दिए गए उदाहरण में। था abcdएक बयान मैं लिख समाप्त नहीं किया का हिस्सा बनने के लिए जा रहा? क्या यह मेरे कोड में एक ब्याह / संपादन का अवशेष है और इसे हटाने की आवश्यकता है? क्या मैंने गलती से पेस्ट किया / कहीं और से कुछ हिलाया?

function(){
  #do stuff
  ...
  return(abdc)
}

इसके विपरीत, यह दूसरा उदाहरण मेरे लिए यह स्पष्ट करता है कि यह किसी दुर्घटना या अधूरे कोड के बजाय एक इच्छित रिटर्न वैल्यू है। मेरे लिए यह अतिरेक बिल्कुल बेकार नहीं है।

बेशक, एक बार कार्य पूरा हो जाने और काम करने के बाद मैं वापसी को हटा सकता था। लेकिन इसे हटाना अपने आप में एक निरर्थक अतिरिक्त कदम है, और मेरे विचार return()में पहली जगह में शामिल करने से ज्यादा बेकार है ।

सभी ने कहा, मैं return()छोटे अनाम वन-लाइनर कार्यों में उपयोग नहीं करता हूं । वहाँ यह फ़ंक्शन के कोड का एक बड़ा हिस्सा बनाता है और इसलिए ज्यादातर दृश्य अव्यवस्था का कारण बनता है जो कोड को कम सुपाठ्य बनाता है। लेकिन औपचारिक रूप से परिभाषित और नामित कार्यों के लिए, मैं इसका उपयोग करता हूं और संभवतः ऐसा करना जारी रखेगा।


"Abcd एक ऐसे कथन का हिस्सा बनने जा रहा था जिसे मैंने लिखना समाप्त नहीं किया था?" - यह आपके द्वारा लिखी गई किसी भी अन्य अभिव्यक्ति से अलग कैसे है , हालांकि? यह, मुझे लगता है कि हमारी असहमति का मूल है। अपने आप में एक परिवर्तनीय स्टैंड होना एक अनिवार्य प्रोग्रामिंग भाषा में अजीब हो सकता है, लेकिन यह पूरी तरह से सामान्य है और एक कार्यात्मक प्रोग्रामिंग भाषा में अपेक्षित है। मुद्दा, मेरा दावा है, बस यह है कि आप कार्यात्मक प्रोग्रामिंग से परिचित नहीं हैं (तथ्य यह है कि आप "अभिव्यक्तियों" के बजाय "बयान" के बारे में बात करते हैं) यह पुष्ट करता है।
कोनराड रूडोल्फ

यह अलग है क्योंकि प्रत्येक दूसरा कथन आमतौर पर कुछ अधिक स्पष्ट तरीके से कुछ करता है: यह एक असाइनमेंट, एक तुलना, एक फ़ंक्शन कॉल है ... हां मेरे पहले कोडिंग चरण अनिवार्य भाषाओं में थे और मैं अभी भी अनिवार्य भाषाओं का उपयोग करता हूं। भाषाओं में समान दृश्य संकेत होने के बावजूद (जहाँ भी भाषाएँ इसकी अनुमति देती हैं) मेरा काम आसान कर देती है। A return(), R में कुछ भी नहीं खर्च करता है। यह वास्तव में बेमानी है, लेकिन "बेकार" होना आपका व्यक्तिपरक निर्णय है। अनावश्यक और बेकार जरूरी समानार्थक शब्द नहीं हैं। जहां हम असहमत हैं।
cymon

इसके अलावा, मैं एक सॉफ्टवेयर इंजीनियर और न ही कंप्यूटर वैज्ञानिक नहीं हूं। शब्दावली के मेरे उपयोग में बहुत अधिक बारीकियों को न पढ़ें।
cymon

बस स्पष्ट करने के लिए: “निरर्थक और बेकार जरूरी समानार्थक शब्द नहीं हैं। जहां हम असहमत हैं। " - नहीं, मैं इससे पूरी तरह सहमत हूं, और मैंने अपने जवाब में स्पष्ट रूप से यह बात कही है। अतिरेक सहायक या निर्णायक भी हो सकता है । लेकिन यह सक्रिय रूप से दिखाए जाने की जरूरत है, न कि माना जाता है। मैं आपके तर्क को समझता हूं कि आप ऐसा क्यों सोचते हैं returnऔर भले ही मुझे यकीन न हो कि मुझे लगता है कि यह संभावित रूप से मान्य है (यह निश्चित रूप से अनिवार्य भाषा में है ... मेरा मानना ​​है कि यह कार्यात्मक भाषाओं में अनुवाद नहीं करता है)।
कोनराड रुडोल्फ
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.