स्काला में विधि और कार्य के बीच अंतर


254

मैंने स्काला फ़ंक्शंस ( स्काला के दूसरे दौरे का हिस्सा ) पढ़ा । उस पोस्ट में उन्होंने कहा:

तरीके और कार्य एक ही चीज नहीं हैं

लेकिन उन्होंने इसके बारे में कुछ भी नहीं बताया। वह क्या कहना चाह रहा था?




अच्छे उत्तरों के साथ एक अनुवर्ती प्रश्न: स्काला में कार्य बनाम विधियाँ
योशिय्याह योडर

जवाबों:


238

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

सबसे पहले, देखते हैं कि स्काला स्पेसिफिकेशन हमें क्या बताता है। अध्याय 3 (प्रकार) हमें फ़ंक्शन प्रकार (3.2.9) और विधि प्रकार (3.3.1) के बारे में बताते हैं । अध्याय 4 (मूल घोषणाएँ) मूल्य घोषणा और परिभाषाएँ (4.1), परिवर्तनीय घोषणा और परिभाषाएँ (4.2) और कार्य घोषणाएँ और परिभाषाएँ (4.6) बोलती हैं । अध्याय 6 (अभिव्यक्ति) बेनामी फ़ंक्शंस (6.23) और विधि मान (6.7) की बात करता है। उत्सुकता से, फ़ंक्शन मान 3.2.9 पर एक समय की बात की जाती है, और कोई और नहीं।

एक फ़ंक्शन प्रकार (लगभग) एक प्रकार का फॉर्म (T1, ..., Tn) => U है , जो FunctionNमानक पुस्तकालय में विशेषता के लिए एक शॉर्टहैंड है । बेनामी फ़ंक्शंस और विधि मानों में फ़ंक्शन प्रकार होते हैं, और फ़ंक्शन प्रकारों का उपयोग मूल्य, चर और फ़ंक्शन घोषणाओं और परिभाषाओं के हिस्से के रूप में किया जा सकता है। वास्तव में, यह एक विधि प्रकार का हिस्सा हो सकता है।

एक विधि प्रकार एक गैर-मूल्य प्रकार है । इसका मतलब है कि कोई विधि - कोई वस्तु, कोई उदाहरण नहीं है - एक विधि प्रकार के साथ। जैसा कि ऊपर उल्लेख किया गया है, एक विधि मान वास्तव में एक फ़ंक्शन प्रकार है । एक विधि प्रकार एक defघोषणा है - defइसके शरीर को छोड़कर सब कुछ ।

मूल्य घोषणाएं और परिभाषाएँ और चर घोषणा और परिभाषाएं हैं valऔर varघोषणाओं, दोनों सहित प्रकार और मान - जो हो सकता है, क्रमशः, समारोह प्रकार और बेनामी कार्य या विधि मान । ध्यान दें कि, JVM पर, ये (विधि मान) जावा को "विधियों" के साथ लागू किया जाता है।

एक समारोह घोषणा एक है defसहित घोषणा, प्रकार और शरीर । टाइप पार्ट मेथड टाइप है, और बॉडी एक एक्सप्रेशन या ब्लॉक है । यह JVM पर भी लागू होता है, जिसे Java "मेथड्स" कहता है।

अंत में, एक बेनामी फंक्शन एक फंक्शन टाइप का उदाहरण है (यानी, विशेषता का एक उदाहरण FunctionN), और एक विधि मान एक ही बात है! भेद यह है कि विधियों से एक विधि मान बनाया जाता है, या तो एक अंडरस्कोर पोस्टफ़िक्स करके ( m _"फ़ंक्शन डिक्लेरेशन" ( def) m), या एटा-विस्तार नामक एक प्रक्रिया द्वारा एक विधि मान है , जो विधि के लिए एक स्वचालित कलाकारों की तरह है कार्य करना।

यह चश्मा क्या कहता है, इसलिए मुझे इस बात को सामने रखना चाहिए: हम उस शब्दावली का उपयोग नहीं करते हैं! यह तथाकथित "फ़ंक्शन डिक्लेरेशन" के बीच बहुत अधिक भ्रम पैदा करता है , जो प्रोग्राम का एक हिस्सा है (अध्याय 4 - मूल घोषणाएं) और "अनाम फ़ंक्शन" , जो एक अभिव्यक्ति है, और "फ़ंक्शन प्रकार" , जो है, एक प्रकार - एक विशेषता।

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

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

एक समारोह एक वस्तु में से एक भी शामिल है FunctionXजैसे लक्षण, Function0, Function1, Function2, आदि यह भी शामिल है हो सकता है PartialFunctionके रूप में अच्छी तरह से जो वास्तव में फैली हुई है, Function1

आइए इन लक्षणों में से एक के लिए टाइप हस्ताक्षर देखें:

trait Function2[-T1, -T2, +R] extends AnyRef

इस विशेषता में एक सार विधि है (इसमें कुछ ठोस तरीके भी हैं):

def apply(v1: T1, v2: T2): R

और वह सब हमें बताएं कि इसके बारे में जानना है। एक फ़ंक्शन में एक applyविधि होती है जो प्रकार T1 , T2 , ..., TN के एन मापदंडों को प्राप्त करती है , और कुछ प्रकार का रिटर्न देती है । यह प्राप्त मापदंडों पर कॉन्ट्रैक्ट-वेरिएंट है, और परिणाम पर सह-वेरिएंट है।R

उस विचरण का अर्थ है कि Function1[Seq[T], String]एक उप-प्रकार है Function1[List[T], AnyRef]। उपप्रकार होने का मतलब है कि इसका उपयोग इसके स्थान पर किया जा सकता है। कोई भी आसानी से देख सकता है कि अगर मैं कॉल करने जा रहा हूं f(List(1, 2, 3))और AnyRefबैक की उम्मीद कर रहा हूं , तो ऊपर दिए गए दो प्रकारों में से कोई भी काम करेगा।

अब, एक विधि और एक फ़ंक्शन की समानता क्या है ? ठीक है, अगर fएक फ़ंक्शन है और mगुंजाइश के लिए एक विधि स्थानीय है, तो दोनों को इस तरह कहा जा सकता है:

val o1 = f(List(1, 2, 3))
val o2 = m(List(1, 2, 3))

ये कॉल वास्तव में अलग हैं, क्योंकि पहला एक सिंटैक्टिक शुगर है। स्काला इसका विस्तार करता है:

val o1 = f.apply(List(1, 2, 3))

जो, ज़ाहिर है, ऑब्जेक्ट पर एक विधि कॉल है f। फ़ंक्शंस में इसके लाभ के लिए अन्य सिंटैक्टिक शर्करा भी हैं: फ़ंक्शन शाब्दिक (उनमें से दो, वास्तव में) और (T1, T2) => Rहस्ताक्षर टाइप करें। उदाहरण के लिए:

val f = (l: List[Int]) => l mkString ""
val g: (AnyVal) => String = {
  case i: Int => "Int"
  case d: Double => "Double"
  case o => "Other"
}

एक विधि और एक फ़ंक्शन के बीच एक और समानता यह है कि पूर्व को आसानी से बाद में परिवर्तित किया जा सकता है:

val f = m _

स्काला का विस्तार होगा , यह मानते हुए कि यह mप्रकार है (List[Int])AnyRef(स्केल 2.7):

val f = new AnyRef with Function1[List[Int], AnyRef] {
  def apply(x$1: List[Int]) = this.m(x$1)
}

स्कैला 2.8 पर, यह वास्तव में AbstractFunction1कक्षा के आकार को कम करने के लिए एक वर्ग का उपयोग करता है ।

ध्यान दें कि कोई एक फ़ंक्शन से किसी विधि तक - के आसपास दूसरे तरीके को परिवर्तित नहीं कर सकता है।

तरीकों, हालांकि, एक बड़ा फायदा है (अच्छी तरह से, दो - वे थोड़ा तेज हो सकते हैं): वे टाइप पैरामीटर प्राप्त कर सकते हैं । उदाहरण के लिए, जबकि fऊपर आवश्यक रूप से निर्दिष्ट कर सकते हैं कि Listयह किस प्रकार प्राप्त करता है ( List[Int]उदाहरण में), mइसे मानकीकृत कर सकता है:

def m[T](l: List[T]): String = l mkString ""

मुझे लगता है कि यह बहुत कुछ सब कुछ कवर करता है, लेकिन मैं किसी भी प्रश्न के उत्तर के साथ इसे पूरा करने में प्रसन्न रहूंगा।


26
यह स्पष्टीकरण बहुत स्पष्ट है। बहुत बढ़िया। दुर्भाग्य से ओडस्की / वेन्नर्स / स्पून बुक और स्काला स्पेसी दोनों शब्द "फंक्शन" और "मेथड" का कुछ हद तक परस्पर उपयोग करते हैं। (वे "फ़ंक्शन" कहने के लिए सबसे अधिक पसंद करते हैं, जहां "विधि" स्पष्ट होगी, लेकिन कभी-कभी यह दूसरे तरीके से भी होता है, उदाहरण के लिए, कल्पना की धारा 6.7, जो कार्य करने के तरीकों को परिवर्तित करती है, को "विधि मान" नाम दिया गया है। ।) मुझे लगता है कि इन शब्दों के ढीले उपयोग से लोगों को भाषा सीखने की कोशिश करने पर बहुत भ्रम हो गया है।
सेठ टिस्यू

4
@ सेठ मुझे पता है, मुझे पता है - पिनएस वह किताब थी जिसने मुझे स्काला सिखाया था। मैंने बेहतर तरीके से बेहतर तरीके से सीखा, यानी, मुझे सीधे सेट किया।
डैनियल सी। सोबरल

4
महान व्याख्या! मेरे पास जोड़ने के लिए एक चीज है: जब आप val f = mसंकलक द्वारा विस्तार का उद्धरण करते हैं , जैसा कि val f = new AnyRef with Function1[List[Int], AnyRef] { def apply(x$1: List[Int]) = this.m(x$1) }आपको इंगित करना चाहिए कि thisअंदर की applyविधि AnyRefऑब्जेक्ट को संदर्भित नहीं करती है, लेकिन उस वस्तु को जिसका विधि में val f = m _मूल्यांकन किया गया है ( बाहरी this , इसलिए कहने के लिए ), चूंकि thisक्लोजर द्वारा कैप्चर किए गए मानों में से है (जैसे returnनीचे दिए गए बिंदु के अनुसार)।
होल्गर पीइन

1
@ डैनियल सी.सोबरल, आपके द्वारा बताई गई पीएनएस पुस्तक क्या है? मुझे स्काला सीखने में भी दिलचस्पी है, और उस नाम के साथ एक किताब नहीं मिली है,
tldr

5
@ ओल्डस्की एट द्वारा स्काला में @ टीएलआर प्रोग्रामिंग । यह इसके लिए सामान्य संक्षिप्त नाम है (उन्होंने मुझे बताया कि वे किसी कारण से काफी पसंद नहीं करते थे! :)
डैनियल सी। सोबरल

67

एक विधि और एक फ़ंक्शन के बीच एक बड़ा व्यावहारिक अंतर यह है कि returnइसका क्या मतलब है। returnकेवल कभी एक विधि से लौटता है। उदाहरण के लिए:

scala> val f = () => { return "test" }
<console>:4: error: return outside method definition
       val f = () => { return "test" }
                       ^

एक विधि से परिभाषित फ़ंक्शन से वापस लौटना एक गैर-स्थानीय रिटर्न है:

scala> def f: String = {                 
     |    val g = () => { return "test" }
     | g()                               
     | "not this"
     | }
f: String

scala> f
res4: String = test

जबकि एक स्थानीय विधि से लौटने पर केवल उस विधि से वापसी होती है।

scala> def f2: String = {         
     | def g(): String = { return "test" }
     | g()
     | "is this"
     | }
f2: String

scala> f2
res5: String = is this

9
ऐसा इसलिए है क्योंकि रिटर्न क्लोजर द्वारा कब्जा कर लिया गया है।
डैनियल सी। सोबराल

4
मैं एक बार भी नहीं सोच सकता कि मैं एक समारोह से गैर-व्यावसायिक दायरे में वापस लौटना चाहता हूं। वास्तव में, मैं देख सकता हूं कि एक गंभीर सुरक्षा मुद्दा होने के नाते यदि कोई फ़ंक्शन सिर्फ यह तय कर सकता है कि वह स्टैक को वापस जाना चाहता है। लोंगो की तरह महसूस करता है, केवल गलती से गलत तरीके से आसान हो जाता है। मैंने देखा है कि खोपड़ी मुझे कार्यों से वापस नहीं आने देगी, हालाँकि। इसका मतलब यह है कि यह घृणा भाषा से मारा गया है?
रूट

2
@root - क्या अंदर से लौटने के बारे में for (a <- List(1, 2, 3)) { return ... }? कि एक बंद करने के लिए de-sugared हो जाता है।
बेन लिंग्स

हम्म ... खैर, यह एक उचित उपयोग मामला है। अभी भी भयानक से कठिन डिबग समस्याओं के लिए नेतृत्व करने की क्षमता है, लेकिन यह इसे अधिक समझदार संदर्भ में रखता है।
रूट

1
ईमानदारी से मैं अलग वाक्यविन्यास का उपयोग करूँगा। है returnसमारोह से कोई मान है, और किसी न किसी रूप escapeया breakया continueतरीकों से वापस जाने के लिए।
रयान द लीच

38

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

स्कैला सेकेंड एडिशन में प्रोग्रामिंग। मार्टिन ओडस्की - लेक्स स्पून - बिल वेनर्स


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

29

मान लीजिए कि आपके पास एक सूची है

scala> val x =List.range(10,20)
x: List[Int] = List(10, 11, 12, 13, 14, 15, 16, 17, 18, 19)

एक विधि परिभाषित करें

scala> def m1(i:Int)=i+2
m1: (i: Int)Int

एक कार्य को परिभाषित करें

scala> (i:Int)=>i+2
res0: Int => Int = <function1>

scala> x.map((x)=>x+2)
res2: List[Int] = List(12, 13, 14, 15, 16, 17, 18, 19, 20, 21)

विधि तर्क स्वीकार करना

scala> m1(2)
res3: Int = 4

घाटी के साथ कार्य को परिभाषित करना

scala> val p =(i:Int)=>i+2
p: Int => Int = <function1>

कार्य करने के लिए तर्क वैकल्पिक है

 scala> p(2)
    res4: Int = 4

scala> p
res5: Int => Int = <function1>

पद्धति का तर्क अनिवार्य है

scala> m1
<console>:9: error: missing arguments for method m1;
follow this method with `_' if you want to treat it as a partially applied function

निम्नलिखित ट्यूटोरियल की जाँच करें जो अन्य अंतरों को उदाहरणों के साथ समझाता है जैसे कि विधि बनाम कार्य के साथ भिन्नता के अन्य उदाहरण, फ़ंक्शन का उपयोग चर के रूप में, जो लौटे फ़ंक्शन का निर्माण करते हैं।


13

कार्य पैरामीटर चूक का समर्थन नहीं करते हैं। तरीके करते हैं। एक विधि से एक फ़ंक्शन में परिवर्तित करना पैरामीटर चूक को खो देता है। (स्काला 2.8.1)


5
क्या इसके कारण हैं?
कोरजा

7

यहाँ एक अच्छा लेख है जहाँ से मेरे अधिकांश विवरण लिए गए हैं। मेरी समझ से संबंधित कार्यों और विधियों की बस थोड़ी सी तुलना। आशा करता हूँ की ये काम करेगा:

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

val f1 = (x: Int) => x + x
f1(2)  // 4

ऊपर की पंक्ति ऑब्जेक्ट 1 = ऑब्जेक्ट 2 जैसी एक वस्तु को दूसरे को असाइन करने के अलावा कुछ भी नहीं है। वास्तव में हमारे उदाहरण में object2 एक अनाम फ़ंक्शन है और बाईं ओर ऑब्जेक्ट उसी के कारण मिलता है। इसलिए, अब f1 एक ऑब्जेक्ट (फ़ंक्शन) है। अनाम फ़ंक्शन वास्तव में Function1 [Int, Int] का एक उदाहरण है, जिसका अर्थ है एक प्रकार का Int और वापसी मान टाइप 1 का पैरामीटर। तर्कों के बिना f1 को कॉल करने से हमें अनाम फ़ंक्शन (Int => Int =) का हस्ताक्षर मिलेगा

विधियाँ : वे एक वस्तु नहीं हैं, बल्कि एक वर्ग के उदाहरण के लिए दी गई हैं, अर्थात, एक वस्तु। वास्तव में j ++ या c ++ में सदस्य कार्यों के रूप में एक ही विधि (जैसा कि रफ़ी खाचदौरीयन ने इस प्रश्न के लिए एक टिप्पणी में बताया है ) और आदि। एक विधि का एक सरल उदाहरण केवल bellow की तरह है:

def m1(x: Int) = x + x
m1(2)  // 4

ऊपर दी गई लाइन एक साधारण मान असाइनमेंट नहीं है, बल्कि एक विधि की परिभाषा है। जब आप दूसरी पंक्ति की तरह मान 2 के साथ इस विधि को लागू करते हैं, तो x को 2 के साथ प्रतिस्थापित किया जाता है और परिणाम की गणना की जाएगी और आपको आउटपुट के रूप में 4 मिलता है। यहां आपको एक त्रुटि मिलेगी अगर बस एम 1 लिखो क्योंकि यह विधि है और इनपुट मूल्य की आवश्यकता है। _ का उपयोग करके आप किसी फ़ंक्शन को bellow जैसी विधि असाइन कर सकते हैं:

val f2 = m1 _  // Int => Int = <function1>

"फ़ंक्शन को एक विधि निर्दिष्ट करने" का क्या मतलब है? क्या इसका मतलब सिर्फ इतना है कि आपके पास अब एक वस्तु है जो उसी तरीके से व्यवहार करती है जैसे कि विधि ने किया था?
। M

@ केएम: वैल एफ 2 = एम 1 _ वैल एफ 2 = नए फंक्शन 1 के बराबर है [इंट, इंट] {डीएफ एम 1 (एक्स: इंट) = एक्स + एक्स};
ससुके

3

यहां रोब नोरिस द्वारा एक महान पोस्ट है जो अंतर को समझाता है, यहां एक टीएल; डीआर है

स्काला के तरीके मूल्य नहीं हैं, लेकिन कार्य हैं। आप एक ऐसे कार्य का निर्माण कर सकते हैं जो method-विस्तार (अनुगामी अंडरस्कोर थैली द्वारा ट्रिगर) के माध्यम से एक विधि को दर्शाता है।

निम्नलिखित परिभाषा के साथ:

एक विधि कुछ के साथ परिभाषित किया गया है डीईएफ़ और एक मूल्य कुछ आप एक को निर्दिष्ट कर सकते वैल

संक्षेप में ( ब्लॉग से निकालें ):

जब हम एक विधि को परिभाषित करते हैं तो हम देखते हैं कि हम इसे असाइन नहीं कर सकते हैं val

scala> def add1(n: Int): Int = n + 1
add1: (n: Int)Int

scala> val f = add1
<console>:8: error: missing arguments for method add1;
follow this method with `_' if you want to treat it as a partially applied function
       val f = add1

नोट भी प्रकार है add1, जो सामान्य नहीं लगता है; आप प्रकार का एक चर घोषित नहीं कर सकते (n: Int)Int। तरीके मूल्य नहीं हैं।

हालांकि, η-विस्तार पोस्टफिक्स ऑपरेटर (pron उच्चारण "एटा") को जोड़कर, हम विधि को फ़ंक्शन मान में बदल सकते हैं। के प्रकार पर ध्यान दें f

scala> val f = add1 _
f: Int => Int = <function1>

scala> f(3)
res0: Int = 4

इसका प्रभाव _निम्नलिखित के बराबर प्रदर्शन करना है: हम एक Function1उदाहरण का निर्माण करते हैं जो हमारी पद्धति को दर्शाता है।

scala> val g = new Function1[Int, Int] { def apply(n: Int): Int = add1(n) }
g: Int => Int = <function1>

scala> g(3)
res18: Int = 4

1

स्कैला 2.13 में, कार्यों के विपरीत, विधियाँ ले / लौट सकती हैं

  • प्रकार के पैरामीटर (बहुरूपी तरीके)
  • निहित मापदंडों
  • निर्भर प्रकार

हालाँकि, इन प्रतिबंधों को Polymorphic function type # 4672 द्वारा dotty (Scala 3) में उठाया गया है , उदाहरण के लिए, dotty संस्करण 0.23.0-RC1 निम्नलिखित सिंटैक्स को सक्षम करता है

प्रकार के पैरामीटर

def fmet[T](x: List[T]) = x.map(e => (e, e))
val ffun = [T] => (x: List[T]) => x.map(e => (e, e))

निहित पैरामीटर ( संदर्भ पैरामीटर)

def gmet[T](implicit num: Numeric[T]): T = num.zero
val gfun: [T] => Numeric[T] ?=> T = [T] => (using num: Numeric[T]) => num.zero

आश्रित प्रकार

class A { class B }
def hmet(a: A): a.B = new a.B
val hfun: (a: A) => a.B = hmet

अधिक उदाहरणों के लिए, परीक्षण / रन / पॉलीमॉर्फिक-फ़ंक्शन.काला देखें


0

व्यावहारिक रूप से, एक स्काला प्रोग्रामर को केवल फ़ंक्शन और विधियों का ठीक से उपयोग करने के लिए निम्नलिखित तीन नियमों को जानना होगा:

  • defकार्य द्वारा परिभाषित और कार्य शाब्दिक द्वारा परिभाषित तरीके =>कार्य हैं। इसे पृष्ठ 143, अध्याय 8 में स्कैला में प्रोग्रामिंग की पुस्तक में 4 वें संस्करण में परिभाषित किया गया है।
  • फ़ंक्शन मान वे ऑब्जेक्ट हैं जिन्हें किसी भी मान के रूप में पास किया जा सकता है। फ़ंक्शन शाब्दिक और आंशिक रूप से लागू फ़ंक्शन फ़ंक्शन मान हैं।
  • यदि कोड में एक बिंदु पर एक फ़ंक्शन मान आवश्यक है, तो आप आंशिक रूप से लागू फ़ंक्शन के अंडरस्कोर को छोड़ सकते हैं। उदाहरण के लिए:someNumber.foreach(println)

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

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