स्काला में मूल्य बनाम नाम से कॉल, स्पष्टीकरण की आवश्यकता


239

जैसा कि मैं इसे समझता हूं, स्काला में, एक फ़ंक्शन को भी बुलाया जा सकता है

  • मूल्य द्वारा या
  • नाम से

उदाहरण के लिए, निम्नलिखित घोषणाओं को देखते हुए, क्या हम जानते हैं कि फ़ंक्शन को कैसे कहा जाएगा?

घोषणा:

def  f (x:Int, y:Int) = x;

कॉल

f (1,2)
f (23+55,5)
f (12+3, 44*11)

कृपया नियम क्या हैं?

जवाबों:


540

आपके द्वारा दिया गया उदाहरण केवल कॉल-बाय-वैल्यू का उपयोग करता है, इसलिए मैं एक नया, सरल, उदाहरण दूंगा जो अंतर दिखाता है।

सबसे पहले, मान लें कि हमारे पास साइड-इफेक्ट वाला एक फ़ंक्शन है। यह फ़ंक्शन कुछ प्रिंट करता है और फिर एक रिटर्न करता है Int

def something() = {
  println("calling something")
  1 // return value
}

अब हम दो फ़ंक्शन को परिभाषित करने जा रहे हैं जो Intतर्क को स्वीकार करते हैं जो वास्तव में समान हैं सिवाय इसके कि एक कॉल-बाय-वैल्यू स्टाइल में तर्क लेता है ( x: Int) और दूसरा कॉल-बाय-नेम स्टाइल ( x: => Int) में।

def callByValue(x: Int) = {
  println("x1=" + x)
  println("x2=" + x)
}

def callByName(x: => Int) = {
  println("x1=" + x)
  println("x2=" + x)
}

अब क्या होता है जब हम उन्हें अपने पक्ष प्रभाव वाले फ़ंक्शन के साथ कहते हैं?

scala> callByValue(something())
calling something
x1=1
x2=1

scala> callByName(something())
calling something
x1=1
calling something
x2=1

तो आप देख सकते हैं कि कॉल-बाय-वैल्यू संस्करण में, पास-इन फ़ंक्शन कॉल ( something()) का साइड-इफेक्ट केवल एक बार हुआ। हालांकि, कॉल-बाय-नेम संस्करण में, साइड-इफेक्ट दो बार हुआ।

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


296
मैंने हमेशा सोचा है कि यह शब्दावली अनावश्यक रूप से भ्रमित करने वाली है। एक फ़ंक्शन में कई पैरामीटर हो सकते हैं जो उनके कॉल-बाय-नाम बनाम कॉल-बाय-वैल्यू स्थिति में भिन्न होते हैं। अत: यह एक है कि नहीं है समारोह कॉल-दर-नाम या कॉल-दर-मूल्य है, यह है कि इसकी से प्रत्येक है मापदंडों किया जा सकता है पारित -by-नाम या पारित-दर-मूल्य। इसके अलावा, "कॉल-बाय-नेम" का नामों से कोई लेना-देना नहीं है । => Intसे एक अलग प्रकार है Int; यह "कोई तर्क है कि उत्पन्न होगा एक के समारोह है Intबनाम बस" Int। एक बार प्रथम श्रेणी के कार्य करने के बाद आपको इसका वर्णन करने के लिए कॉल-बाय-नेम शब्दावली का आविष्कार करने की आवश्यकता नहीं है
बेन

2
@, जो कुछ सवालों के जवाब देने में मदद करता है, धन्यवाद। मैं चाहता हूं कि अधिक लेखन ने इसे स्पष्ट रूप से पास-नाम के शब्दार्थ को समझाया।
क्रिस्टोफर पोइल

3
@SelimOber यदि पाठ f(2)को प्रकार की अभिव्यक्ति के रूप में संकलित किया जाता है Int, तो उत्पन्न कोड fतर्क के साथ कॉल करता है 2और परिणाम अभिव्यक्ति का मूल्य है। यदि उसी पाठ को एक प्रकार की अभिव्यक्ति के रूप में संकलित किया जाता है => Intतो उत्पन्न कोड अभिव्यक्ति के मूल्य के रूप में "कोड ब्लॉक" के कुछ प्रकार के संदर्भ का उपयोग करता है। किसी भी तरह से, उस प्रकार का एक मान उस प्रकार के पैरामीटर की अपेक्षा कर एक फ़ंक्शन को पास किया जा सकता है। मुझे पूरा यकीन है कि आप इसे चर असाइनमेंट के साथ कर सकते हैं, जिसमें कोई पैरामीटर दृष्टि से गुजरने वाला नहीं है। तो क्या नाम या कॉल का इससे कोई लेना-देना है?
बेन

4
@ तो अगर => Int"बिना किसी तर्क के कार्य करता है जो एक Int उत्पन्न करता है", यह कैसे अलग है () => Int? इनका इलाज अलग तरह से होता है, उदाहरण के लिए, => Intस्पष्ट रूप से valकेवल एक पैरामीटर के प्रकार के रूप में काम नहीं करता है ।
टिम गुडमैन

5
@TimGoodman आप सही हैं, यह थोड़ा अधिक जटिल है जितना मैंने बनाया है। => Intएक सुविधा है, और यह एक फ़ंक्शन ऑब्जेक्ट के रूप में बिल्कुल लागू नहीं किया जाता है (संभवतः इसलिए आपके पास प्रकार के चर नहीं हो सकते हैं => Int, हालांकि कोई मौलिक कारण नहीं है कि यह काम क्यों नहीं कर सका)। () => Intहै स्पष्ट रूप से कोई तर्क है कि एक वापस आ जाएगी के एक समारोह Intहै, जो स्पष्ट रूप से कहा जाता है की जरूरत है और एक समारोह के रूप पारित किया जा सकता। => Intएक "प्रॉक्सी Int" की तरह है, और केवल एक चीज जिसे आप इसके साथ कर सकते हैं उसे कॉल है (संक्षेप में) Int
बेन

51

यहाँ मार्टिन ओडस्की का एक उदाहरण दिया गया है:

def test (x:Int, y: Int)= x*x

हम मूल्यांकन रणनीति की जांच करना चाहते हैं और निर्धारित करते हैं कि इन स्थितियों में कौन सा तेज (कम कदम) है:

test (2,3)

मूल्य द्वारा कॉल: परीक्षण (2,3) -> 2 * 2 -> 4
कॉल नाम से: परीक्षण (2,3) -> 2 * 2 -> 4
यहां परिणाम समान चरणों की संख्या के साथ पहुंचा है।

test (3+4,8)

मूल्य से कॉल करें: परीक्षण (7,8) -> 7 * 7 -> 49
कॉल नाम से: (3 + 4) (3 + 4) -> 7 (3 + 4) -> 7 * 7 -> 49
यहां कॉल करें मूल्य से तेज है।

test (7,2*4)

मूल्य से कॉल: परीक्षण (7,8) -> 7 * 7 -> 49
नाम से कॉल: 7 * 7 -> 49
यहाँ नाम से कॉल तेजी से है

test (3+4, 2*4) 

मूल्य से कॉल करें: परीक्षण (7,2 * 4) -> परीक्षण (7, 8) -> 7 * 7 -> 49
कॉल नाम से: (3 + 4) (3 + 4) -> 7 (3 + 4) -> 7 * 7 -> 49
परिणाम समान चरणों के भीतर पहुंच गया है।


1
सीबीवी के लिए तीसरे उदाहरण में, मुझे लगता है कि आपने परीक्षण (
7,14

1
उदाहरण कसेरा से लिया गया है, स्कैला प्रोग्रामिंग में सिद्धांत। व्याख्यान 1.2। नाम से कॉल को def test (x:Int, y: => Int) = x * xध्यान देना चाहिए कि पैरामीटर y का उपयोग कभी नहीं किया जाता है।
डे जेरी

1
अच्छा उदाहरण!
कौरसेरा

यह अंतर की एक अच्छी व्याख्या है, लेकिन यह पूछे जाने वाले प्रश्न के लिए संबोधित नहीं करता है, अर्थात् दोनों में से कौन सा स्काला कॉलिंग है
db1234

16

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

def f(x: => Int, y:Int) = x

इस तरह से पैरामीटर xका मूल्यांकन तब तक नहीं किया जाएगा जब तक कि इसे फ़ंक्शन में नहीं बुलाया जाता है।

यहाँ यह छोटी सी पोस्ट इस बारीकियों को भी समझाती है।


10

उपरोक्त टिप्पणियों में @ बेन की बात को पुन: प्रसारित करने के लिए, मुझे लगता है कि "कॉल-बाय-नेम" को केवल सिन्थेटिक चीनी के रूप में सोचना सबसे अच्छा है। पार्सर केवल अनाम कार्यों में अभिव्यक्तियों को लपेटता है, ताकि उन्हें बाद में बिंदु पर बुलाया जा सके, जब उनका उपयोग किया जाता है।

वास्तव में, परिभाषित करने के बजाय

def callByName(x: => Int) = {
  println("x1=" + x)
  println("x2=" + x)
}

और चल रहा है:

scala> callByName(something())
calling something
x1=1
calling something
x2=1

आप यह भी लिख सकते हैं:

def callAlsoByName(x: () => Int) = {
  println("x1=" + x())
  println("x2=" + x())
}

और इसे उसी प्रभाव के लिए निम्न प्रकार से चलाएं:

callAlsoByName(() => {something()})

calling something
x1=1
calling something
x2=1

मुझे लगता है कि आपका मतलब है: <! - भाषा: lang-scala -> def callAlsoByName (x: () => Int) = {println ("X1 =" + x ()) println ("x_ =" + x) ))} और फिर: <! - भाषा: lang-js -> callAlsoByName (() => कुछ ()) मुझे नहीं लगता कि आपको इस अंतिम कॉल में कुछ () के आसपास घुंघराले ब्रेस की आवश्यकता है। नोट: मैंने केवल आपके उत्तर को संपादित करने की कोशिश की, लेकिन मेरा संपादन समीक्षकों द्वारा यह कहते हुए अस्वीकार कर दिया गया कि इसके बजाय एक टिप्पणी या अलग उत्तर होना चाहिए।
लैंबडिस्टा

जाहिरा तौर पर आप टिप्पणियों में हाइलाइटिंग सिंटैक्स का उपयोग नहीं कर सकते हैं इसलिए केवल "<! - भाषा: लैंग-स्काला ->" भाग को अनदेखा करें! मैंने अपनी टिप्पणी संपादित की होगी, लेकिन आपको इसे केवल 5 मिनट के भीतर करने की अनुमति है! :)
लैम्बिडा

1
मैं हाल ही में इस रूप में अच्छी तरह से भाग गया। यह इस तरह से सोचने के लिए वैचारिक रूप से ठीक है, लेकिन स्कैला बीच => Tऔर में अंतर करती है () => T। एक फ़ंक्शन जो एक पैरामीटर के रूप में पहला प्रकार लेता है, दूसरा स्वीकार नहीं करेगा, स्काला इसके @ScalaSignatureलिए संकलन समय त्रुटि को फेंकने के लिए एनोटेशन में पर्याप्त जानकारी संग्रहीत करता है । दोनों के लिए बाईटकोड => Tऔर () => Tएक ही है, हालांकि है और एक है Function0। देखें इस सवाल का अधिक जानकारी के लिए।
vsnyc

6

मैं केवल एक उदाहरण प्रदान करने के बजाय एक साधारण उपयोग के मामले से समझाने की कोशिश करूंगा

कल्पना कीजिए कि आप "नागर ऐप" का निर्माण करना चाहते हैं, जो हर बार आपको नागवार देगा क्योंकि पिछले समय में आपको नागवार मिला था।

निम्नलिखित कार्यान्वयन की जांच करें:

object main  {

    def main(args: Array[String]) {

        def onTime(time: Long) {
            while(time != time) println("Time to Nag!")
            println("no nags for you!")
        }

        def onRealtime(time: => Long) {
            while(time != time) println("Realtime Nagging executed!")
        }

        onTime(System.nanoTime())
        onRealtime(System.nanoTime())
    }
}

उपर्युक्त क्रियान्वयन में केवल नाम से गुजरते समय काम करने वाला कारण होगा, जब मूल्य से गुजरने पर इसका फिर से उपयोग किया जाएगा और इसलिए नाम से गुजरते समय मूल्य का पुनर्मूल्यांकन नहीं किया जाएगा। समय पर चर तक पहुँचा जाता है


4

आमतौर पर, फ़ंक्शन के पैरामीटर बाय-वैल्यू पैरामीटर हैं; वह है, पैरामीटर का मान फ़ंक्शन में पारित होने से पहले निर्धारित किया जाता है। लेकिन क्या होगा अगर हमें एक फ़ंक्शन लिखने की ज़रूरत है जो एक पैरामीटर के रूप में स्वीकार करता है एक अभिव्यक्ति जिसे हम मूल्यांकन नहीं करना चाहते हैं जब तक कि इसे हमारे फ़ंक्शन के भीतर नहीं कहा जाता है? इस परिस्थिति के लिए, स्काला कॉल-बाय-नेम पैरामीटर प्रदान करता है।

कॉल-बाय-नेम मैकेनिज्म एक कोड ब्लॉक को कैली में पास करता है और हर बार कैली पैरामीटर को एक्सेस करता है, कोड ब्लॉक निष्पादित होता है और मूल्य की गणना की जाती है।

object Test {
def main(args: Array[String]) {
    delayed(time());
}

def time() = {
  println("Getting time in nano seconds")
  System.nanoTime
}
def delayed( t: => Long ) = {
  println("In delayed method")
  println("Param: " + t)
  t
}
}
 1. सी: /> स्कैलेक टेस्ट.काला 
 2. स्केला टेस्ट
 3. विलंबित विधि में
 4. नैनो सेकंड में समय प्राप्त करना
 5. परम: 81303808765843
 6. नैनो सेकंड में समय प्राप्त करना

2

जैसा कि मैं मानता हूं, call-by-valueऊपर चर्चा के रूप में फ़ंक्शन फ़ंक्शन के लिए मानों को पास करता है। इसके अनुसार Martin Oderskyयह एक मूल्यांकन रणनीति है जिसका पालन एक स्कैला द्वारा किया जाता है जो फ़ंक्शन मूल्यांकन में महत्वपूर्ण भूमिका निभाता है। लेकिन, इसे सरल बनाएं call-by-name। यह एक विधि की तरह एक तर्क के रूप में भी समारोह पास के रूप में पता है Higher-Order-Functions। जब विधि पारित पैरामीटर के मूल्य तक पहुंचती है, तो यह पारित कार्यों के कार्यान्वयन को बुलाता है। नीचे के अनुसार:

@Dhg उदाहरण के अनुसार, पहले विधि बनाएं:

def something() = {
 println("calling something")
 1 // return value
}  

इस फ़ंक्शन में एक printlnकथन होता है और पूर्णांक मान लौटाता है। फ़ंक्शन बनाएँ, जिनके पास तर्क हैं call-by-name:

def callByName(x: => Int) = {
 println("x1=" + x)
 println("x2=" + x)
}

यह फ़ंक्शन पैरामीटर, एक अनाम फ़ंक्शन को परिभाषित करता है जिनके पास एक पूर्णांक मान है। इसमें xफ़ंक्शन की एक परिभाषा शामिल है जिन्होंने 0तर्क पारित किए हैं, लेकिन वापसी intमूल्य और हमारे somethingफ़ंक्शन में एक ही हस्ताक्षर हैं। जब हम फ़ंक्शन को कॉल करते हैं, तो हम फ़ंक्शन को तर्क के रूप में पास करते हैं callByName। लेकिन call-by-valueइसके मामले में केवल फ़ंक्शन के लिए पूर्णांक मान पास करें। हम फ़ंक्शन को नीचे के रूप में कहते हैं:

scala> callByName(something())
 calling something
 x1=1
 calling something
 x2=1 

यह हमारे में somethingविधि दो बार कहा जाता है क्योंकि जब हम का मूल्य का उपयोग, xमें callByName, विधि के defintion के अपने कॉल somethingविधि।


2

मूल्य द्वारा कॉल सामान्य उपयोग मामला है जैसा कि यहां कई उत्तरों द्वारा समझाया गया है।

कॉल-बाय-नाम कॉलर को एक कोड ब्लॉक पास करता है और हर बार जब कॉल करने वाला पैरामीटर तक पहुंचता है, तो कोड ब्लॉक निष्पादित होता है और मूल्य की गणना की जाती है।

मैं नीचे उपयोग मामलों के साथ नाम से अधिक सरल तरीके से कॉल प्रदर्शित करने का प्रयास करूंगा

उदाहरण 1:

नाम से कॉल का सरल उदाहरण / उपयोग का मामला फ़ंक्शन से नीचे है, जो फ़ंक्शन को पैरामीटर के रूप में लेता है और बीता हुआ समय देता है।

 /**
   * Executes some code block and prints to stdout the 
time taken to execute   the block 
for interactive testing and debugging.
   */
  def time[T](f: => T): T = {
    val start = System.nanoTime()
    val ret = f
    val end = System.nanoTime()

    println(s"Time taken: ${(end - start) / 1000 / 1000} ms")

    ret
  }

उदाहरण 2:

अपाचे स्पार्क (स्केला के साथ) नाम का उपयोग करके लॉगिंग का उपयोग करता है जिस तरह से Loggingविशेषता देखते हैं जिसमें इसका आलसी मूल्यांकन करता है कि log.isInfoEnabledनीचे की विधि से या नहीं।

protected def logInfo(msg: => String) {
     if (log.isInfoEnabled) log.info(msg)
 }

2

एक में से पैसे कॉल , अभिव्यक्ति के मूल्य समारोह कॉल के समय पूर्व गणना की और उस विशेष मान इसी समारोह के लिए पैरामीटर के रूप में पारित हो जाता है है। पूरे फ़ंक्शन में समान मान का उपयोग किया जाएगा।

जबकि Call by Name में , अभिव्यक्ति को फ़ंक्शन के पैरामीटर के रूप में पास किया जाता है और यह केवल फ़ंक्शन के अंदर ही गणना की जाती है, जब भी उस विशेष पैरामीटर को कहा जाता है।

स्काला में कॉल बाय नेम और वैल्यू बाय वैल्यू के बीच के अंतर को नीचे दिए गए उदाहरण से समझा जा सकता है:

सांकेतिक टुकड़ा

object CallbyExample extends App {

  // function definition of call by value
  def CallbyValue(x: Long): Unit = {
    println("The current system time via CBV: " + x);
    println("The current system time via CBV " + x);
  }

  // function definition of call by name
  def CallbyName(x: => Long): Unit = {
    println("The current system time via CBN: " + x);
    println("The current system time via CBN: " + x);
  }

  // function call
  CallbyValue(System.nanoTime());
  println("\n")
  CallbyName(System.nanoTime());
}

उत्पादन

The current system time via CBV: 1153969332591521
The current system time via CBV 1153969332591521


The current system time via CBN: 1153969336749571
The current system time via CBN: 1153969336856589

फ़ंक्शन कोड CallbyValue (System.nanoTime ()) के लिए उपरोक्त कोड स्निपेट में, सिस्टम नैनो समय की पूर्व-गणना की जाती है और यह कि पूर्व-परिकलित मान फ़ंक्शन कॉल के लिए एक पैरामीटर पारित किया गया है।

लेकिन CallbyName (System.nanoTime ()) फ़ंक्शन कॉल में, अभिव्यक्ति "System.nanoTime ())" स्वयं फ़ंक्शन कॉल के लिए एक पैरामीटर के रूप में पारित किया जाता है और उस पैरामीटर के फ़ंक्शन के अंदर उपयोग किए जाने पर उस एक्सप्रेशन के मूल्य की गणना की जाती है। ।

CallbyName फ़ंक्शन की फ़ंक्शन परिभाषा पर ध्यान दें, जहां पैरामीटर x और इसके डेटाटाइप को अलग करने वाला एक => प्रतीक है । उस विशेष प्रतीक से संकेत मिलता है कि फ़ंक्शन नाम प्रकार से कॉल का है।

दूसरे शब्दों में, फ़ंक्शन को दर्ज करने से पहले एक बार वैल्यू फ़ंक्शन तर्कों का कॉल मूल्यांकन किया जाता है, लेकिन नाम फ़ंक्शन तर्कों द्वारा कॉल को फ़ंक्शन के अंदर केवल तब मूल्यांकन किया जाता है, जब उन्हें ज़रूरत होती है।

उम्मीद है की यह मदद करेगा!


2

यहाँ मेरा एक त्वरित उदाहरण है जो मेरे एक सहयोगी की मदद करने के लिए कोडित किया गया है जो वर्तमान में स्काला पाठ्यक्रम ले रहा है। मैंने जो सोचा वह दिलचस्प था कि मार्टिन ने व्याख्यान में पहले दिए गए && प्रश्न उत्तर का उपयोग एक उदाहरण के रूप में नहीं किया। किसी भी घटना में मुझे आशा है कि यह मदद करता है।

val start = Instant.now().toEpochMilli

val calc = (x: Boolean) => {
    Thread.sleep(3000)
    x
}


def callByValue(x: Boolean, y: Boolean): Boolean = {
    if (!x) x else y
}

def callByName(x: Boolean, y: => Boolean): Boolean = {
    if (!x) x else y
}

new Thread(() => {
    println("========================")
    println("Call by Value " + callByValue(false, calc(true)))
    println("Time " + (Instant.now().toEpochMilli - start) + "ms")
    println("========================")
}).start()


new Thread(() => {
    println("========================")
    println("Call by Name " + callByName(false, calc(true)))
    println("Time " + (Instant.now().toEpochMilli - start) + "ms")
    println("========================")
}).start()


Thread.sleep(5000)

कोड का आउटपुट निम्न होगा:

========================
Call by Name false
Time 64ms
========================
Call by Value false
Time 3068ms
========================

1

पैरामीटर आमतौर पर मूल्य से गुजरते हैं, जिसका अर्थ है कि फ़ंक्शन बॉडी में प्रतिस्थापित होने से पहले उनका मूल्यांकन किया जाएगा।

आप फ़ंक्शन को परिभाषित करते समय एक पैरामीटर को डबल तीर का उपयोग करके नाम से कॉल करने के लिए मजबूर कर सकते हैं।

// first parameter will be call by value, second call by name, using `=>`
def returnOne(x: Int, y: => Int): Int = 1

// to demonstrate the benefits of call by name, create an infinite recursion
def loop(x: Int): Int = loop(x)

// will return one, since `loop(2)` is passed by name so no evaluated
returnOne(2, loop(2))

// will not terminate, since loop(2) will evaluate. 
returnOne(loop(2), 2) // -> returnOne(loop(2), 2) -> returnOne(loop(2), 2) -> ... 

1

इंटरनेट में इस सवाल के लिए पहले से ही बहुत सारे शानदार जवाब हैं। मैं कई स्पष्टीकरणों और उदाहरणों का संकलन लिखूंगा, जो मैंने विषय के बारे में एकत्र किए हैं, बस अगर कोई व्यक्ति इसे उपयोगी समझ सकता है

परिचय

कॉल-बाय-वैल्यू (CBV)

आमतौर पर, फ़ंक्शन के पैरामीटर कॉल-बाय-वैल्यू पैरामीटर हैं; अर्थात्, फंक्शन का मूल्यांकन होने से पहले, उनके मान का निर्धारण करने के लिए मापदंडों का बाएं से दाएं मूल्यांकन किया जाता है

def first(a: Int, b: Int): Int = a
first(3 + 4, 5 + 6) // will be reduced to first(7, 5 + 6), then first(7, 11), and then 7

कॉल-बाय-नेम (CBN)

लेकिन क्या होगा अगर हमें एक फ़ंक्शन लिखने की ज़रूरत है जो एक पैरामीटर के रूप में स्वीकार करता है एक अभिव्यक्ति जिसे हम मूल्यांकन नहीं करते हैं जब तक कि इसे हमारे फ़ंक्शन के भीतर नहीं कहा जाता है? इस परिस्थिति के लिए, स्काला कॉल-बाय-नेम पैरामीटर प्रदान करता है। मतलब पैरामीटर को फंक्शन में पास किया जाता है जैसा कि है, और प्रतिस्थापन के बाद इसका मूल्यांकन होता है

def first1(a: Int, b: => Int): Int = a
first1(3 + 4, 5 + 6) // will be reduced to (3 + 4) and then to 7

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

 object Demo {
       def main(args: Array[String]) {
            delayed(time());
       }
    def time() = {
          println("Getting time in nano seconds")
          System.nanoTime
       }
       def delayed( t: => Long ) = {
          println("In delayed method")
          println("Param: " + t)
       }
    }

विलंबित विधि
में नैनो सेकंड में समय प्राप्त करना
परम: 2027245119786400

प्रत्येक मामले के लिए PROS और CONS

CBN: + अधिक बार टर्मिनेट करता है * टर्मिनेशन के ऊपर नीचे की जाँच करें * + क्या फायदा है कि एक फ़ंक्शन तर्क का मूल्यांकन नहीं किया जाता है यदि फ़ंक्शन बॉडी के मूल्यांकन में संबंधित पैरामीटर अप्रयुक्त है-यह धीमा है, यह अधिक कक्षाएं बनाता है (मतलब प्रोग्राम लेता है) अब लोड करने के लिए) और यह अधिक मेमोरी खपत करता है।

CBV: + यह CBN की तुलना में अक्सर अधिक कुशल होता है, क्योंकि यह बार-बार होने वाले तर्कों के दोहराव से बच जाता है, जिसे नाम से पुकारा जाता है। यह प्रत्येक फ़ंक्शन तर्क का केवल एक बार मूल्यांकन करता है + यह अनिवार्य प्रभावों और दुष्प्रभावों के साथ बहुत अच्छा खेलता है, क्योंकि आप बहुत बेहतर जानते हैं कि जब अभिव्यक्तियों का मूल्यांकन किया जाएगा। -इसके मापदंडों के मूल्यांकन के दौरान एक लूप हो सकता है * समाप्ति से नीचे की जाँच करें *

क्या होगा अगर समाप्ति की गारंटी नहीं है?

-अगर किसी अभिव्यक्ति का CBV मूल्यांकन ई समाप्त होता है, तो CBN का मूल्यांकन भी समाप्त हो जाता है-अन्य दिशा सत्य नहीं है

गैर-समाप्ति उदाहरण

def first(x:Int, y:Int)=x

पहले अभिव्यक्ति पर विचार करें (1, लूप)

CBN: पहला (1, लूप) → 1 CBV: पहला (1, लूप) → इस अभिव्यक्ति के तर्कों को कम करता है। चूंकि एक लूप है, इसलिए यह तर्क को कम कर देता है। यह समाप्त नहीं होता है

प्रत्येक मामले में विभिन्न स्रोतों

आइए एक विधि परीक्षण को परिभाषित करें जो होगा

Def test(x:Int, y:Int) = x * x  //for call-by-value
Def test(x: => Int, y: => Int) = x * x  //for call-by-name

Case1 परीक्षण (2,3)

test(2,3)2*24

चूंकि हम पहले से ही मूल्यांकन किए गए तर्कों के साथ शुरू करते हैं, इसलिए यह कॉल-बाय-वैल्यू और कॉल-बाय-नाम के चरणों की समान राशि होगी

Case2 परीक्षण (3 + 4,8)

call-by-value: test(3+4,8) → test(7,8)7 * 749
call-by-name: (3+4)*(3+4)7 * (3+4)7 * 749

इस मामले में कॉल-बाय-वैल्यू कम चरण करता है

Case3 परीक्षण (7, 2 * 4)

call-by-value: test(7, 2*4) → test(7,8)7 * 749
call-by-name: (7)*(7)49

हम दूसरे तर्क की अनावश्यक गणना से बचते हैं

Case4 परीक्षण (3 + 4, 2 * 4)

call-by-value: test(7, 2*4) → test(7,8)7 * 749
call-by-name: (3+4)*(3+4)7*(3+4)7*749

अलग दृष्टिकोण

सबसे पहले, मान लें कि हमारे पास साइड-इफेक्ट वाला एक फ़ंक्शन है। यह फ़ंक्शन कुछ प्रिंट करता है और फिर एक Int देता है।

def something() = {
  println("calling something")
  1 // return value
}

अब हम दो फ़ंक्शन को परिभाषित करने जा रहे हैं जो Int तर्कों को स्वीकार करते हैं जो वास्तव में एक ही हैं सिवाय इसके कि कोई कॉल-बाय-वैल्यू स्टाइल (x: Int) में तर्क लेता है और दूसरा कॉल-बाय-नेम स्टाइल (x: => इंट)।

def callByValue(x: Int) = {
  println("x1=" + x)
  println("x2=" + x)
}
def callByName(x: => Int) = {
  println("x1=" + x)
  println("x2=" + x)
}

अब क्या होता है जब हम उन्हें अपने पक्ष प्रभाव वाले फ़ंक्शन के साथ कहते हैं?

scala> callByValue(something())
calling something
x1=1
x2=1
scala> callByName(something())
calling something
x1=1
calling something
x2=1

तो आप देख सकते हैं कि कॉल-बाय-वैल्यू संस्करण में, पास-इन फ़ंक्शन कॉल (कुछ ()) का साइड-इफेक्ट एक बार हुआ है। हालांकि, कॉल-बाय-नेम संस्करण में, साइड-इफेक्ट दो बार हुआ।

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

जहां इसका उपयोग CALL-BY-NAME के ​​करीब है

प्रेषक: https://stackoverflow.com/a/19036068/1773841

सरल प्रदर्शन उदाहरण: लॉगिंग।

आइए इस तरह के एक इंटरफेस की कल्पना करें:

trait Logger {
  def info(msg: => String)
  def warn(msg: => String)
  def error(msg: => String)
}

और फिर इस तरह इस्तेमाल किया:

logger.info("Time spent on X: " + computeTimeSpent)

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

सही उदाहरण: तर्क ऑपरेटर।

आपने शायद इस तरह कोड देखा है:

if (ref != null && ref.isSomething)

कल्पना कीजिए कि आप इस तरह से && विधि की घोषणा करेंगे:

trait Boolean {
  def &&(other: Boolean): Boolean
}

फिर, जब भी रेफ अशक्त होता है, तो आपको एक त्रुटि मिलेगी क्योंकि && को पास किए जाने से पहले आइसोलेटिंग को एक अशक्तता पर बुलाया जाएगा। इस कारण से, वास्तविक घोषणा है:

trait Boolean {
  def &&(other: => Boolean): Boolean =
    if (this) this else other
}

1

एक उदाहरण के माध्यम से जाने से आपको अंतर को बेहतर ढंग से समझने में मदद मिलेगी।

चलो एक साधारण फ़ंक्शन को निर्धारित करते हैं जो वर्तमान समय लौटाता है:

def getTime = System.currentTimeMillis

अब हम एक फंक्शन को परिभाषित करेंगे, नाम से , जो एक सेकंड में दो बार देरी से प्रिंट करता है:

def getTimeByName(f: => Long) = { println(f); Thread.sleep(1000); println(f)}

और मूल्य से एक :

def getTimeByValue(f: Long) = { println(f); Thread.sleep(1000); println(f)}

अब प्रत्येक को कॉल करते हैं:

getTimeByName(getTime)
// prints:
// 1514451008323
// 1514451009325

getTimeByValue(getTime)
// prints:
// 1514451024846
// 1514451024846

परिणाम को अंतर स्पष्ट करना चाहिए। स्निपेट यहां उपलब्ध है


0

CallByNameजब भी इस्तेमाल किया जाता है और callByValueजब भी बयान का सामना किया जाता है, तब उसे आमंत्रित किया जाता है।

उदाहरण के लिए:-

मेरे पास एक अनंत लूप है यानी यदि आप इस फ़ंक्शन को निष्पादित करते हैं तो हमें कभी भी scalaसंकेत नहीं मिलेगा ।

scala> def loop(x:Int) :Int = loop(x-1)
loop: (x: Int)Int

एक callByNameफ़ंक्शन loopएक तर्क के रूप में उपरोक्त विधि लेता है और इसका उपयोग कभी भी इसके शरीर के अंदर नहीं किया जाता है।

scala> def callByName(x:Int,y: => Int)=x
callByName: (x: Int, y: => Int)Int

callByNameपद्धति के निष्पादन पर हमें कोई समस्या नहीं मिलती है (हम scalaतुरंत वापस आ जाते हैं) क्योंकि हम नहीं हैं जहां फ़ंक्शन के अंदर लूप फ़ंक्शन का उपयोग किया जाता callByNameहै।

scala> callByName(1,loop(10))
res1: Int = 1
scala> 

एक callByValueफ़ंक्शन loopएक पैरामीटर के रूप में विधि के ऊपर कार्य करता है परिणामस्वरूप फ़ंक्शन या अभिव्यक्ति का मूल्यांकन किया जाता है बाहरी फ़ंक्शन को निष्पादित करने से पहले loopफ़ंक्शन को पुनरावर्ती रूप से निष्पादित किया जाता है और हमें कभी भी scalaशीघ्र वापस नहीं मिलता है ।

scala> def callByValue(x:Int,y:Int) = x
callByValue: (x: Int, y: Int)Int

scala> callByValue(1,loop(1))

0

यह देखो:

    object NameVsVal extends App {

  def mul(x: Int, y: => Int) : Int = {
    println("mul")
    x * y
  }
  def add(x: Int, y: Int): Int = {
    println("add")
    x + y
  }
  println(mul(3, add(2, 1)))
}

y: => Int को नाम से पुकारा जाता है। नाम से कॉल के रूप में क्या जोड़ा गया है (2, 1) जोड़ें। इसका मूल्यांकन आलसी तरीके से किया जाएगा। तो कंसोल पर आउटपुट "ऐड" के बाद "mul" होगा, हालांकि ऐड को पहले कहा जाता है। फंक्शन पॉइंटर पास करने के नाम से कॉल किया जाता है।
अब y से बदलें: => Int to y: Int। कंसोल "ऐड" दिखाएगा जिसके बाद "मुल" होगा! मूल्यांकन का सामान्य तरीका।


-2

मुझे नहीं लगता कि यहां सभी उत्तर सही औचित्य बताते हैं:

मूल्य द्वारा कॉल में तर्कों की गणना केवल एक बार की जाती है:

def f(x : Int, y :Int) = x

// following the substitution model

f(12 + 3, 4 * 11)
f(15, 4194304)
15

आप ऊपर देख सकते हैं कि सभी तर्कों का मूल्यांकन किया जाता है कि क्या जरूरत नहीं है, सामान्य रूप call-by-valueसे तेज हो सकता है लेकिन हमेशा इस मामले में पसंद नहीं है।

अगर मूल्यांकन की रणनीति होती call-by-nameतो विघटन होता:

f(12 + 3, 4 * 11)
12 + 3
15

जैसा कि आप ऊपर देख सकते हैं कि हमें कभी भी मूल्यांकन करने की आवश्यकता नहीं है 4 * 11और इसलिए गणना की थोड़ी बचत हुई जो कभी-कभी फायदेमंद हो सकती है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.