स्काला में करीने के दो तरीके; प्रत्येक के लिए उपयोग-मामला क्या है?


83

मेरे द्वारा बनाए गए स्काला स्टाइल गाइड में कई पैरामीटर सूचियों के आसपास चर्चा चल रही है । मुझे पता चला है कि करीने के दो तरीके हैं , और मैं सोच रहा हूं कि उपयोग के मामले क्या हैं:

def add(a:Int)(b:Int) = {a + b}
// Works
add(5)(6)
// Doesn't compile
val f = add(5)
// Works
val f = add(5)_
f(10) // yields 15

def add2(a:Int) = { b:Int => a + b }
// Works
add2(5)(6)
// Also works
val f = add2(5)
f(10) // Yields 15
// Doesn't compile
val f = add2(5)_

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

उन लोगों से जो इन रूपों का उपयोग करते हैं, दूसरे पर एक रूप का उपयोग करने पर आम सहमति क्या है?

जवाबों:


137

एकाधिक पैरामीटर सूची के तरीके

टाइप इंट्रेंस के लिए

पहले प्रकार के तर्कों के लिए पहले खंड में मापदंडों का उपयोग करके स्थानीय प्रकार की निष्कासन की सहायता के लिए कई पैरामीटर अनुभागों के साथ तरीकों का उपयोग किया जा सकता है जो बाद के अनुभाग में एक तर्क के लिए एक अपेक्षित प्रकार प्रदान करेगा। foldLeftमानक पुस्तकालय में इस का विहित उदाहरण है।

def foldLeft[B](z: B)(op: (B, A) => B): B

List("").foldLeft(0)(_ + _.length)

यदि यह इस रूप में लिखा गया था:

def foldLeft[B](z: B, op: (B, A) => B): B

एक को अधिक स्पष्ट प्रकार प्रदान करने होंगे:

List("").foldLeft(0, (b: Int, a: String) => a + b.length)
List("").foldLeft[Int](0, _ + _.length)

धाराप्रवाह एपीआई के लिए

एकाधिक पैरामीटर अनुभाग विधियों के लिए एक और उपयोग एक एपीआई बनाने के लिए है जो भाषा निर्माण की तरह दिखता है। कॉलर कोष्ठक के बजाय ब्रेसिज़ का उपयोग कर सकता है।

def loop[A](n: Int)(body: => A): Unit = (0 until n) foreach (n => body)

loop(2) {
   println("hello!")
}

N तर्क सूचियों का अनुप्रयोग M पैरामीटर अनुभागों के साथ विधि के लिए, जहां N <M, को एक _प्रकार के साथ या अनुमानित रूप से स्पष्ट रूप से एक फ़ंक्शन में परिवर्तित किया जा सकता है FunctionN[..]। यह एक सुरक्षा सुविधा है, स्कैला संदर्भ में, स्कैला 2.0 के लिए, पृष्ठभूमि के लिए, परिवर्तन नोट देखें।

करी क्रिया

करी कार्यों (या बस, फ़ंक्शन जो फ़ंक्शंस लौटाते हैं) को एन तर्क सूचियों में अधिक आसानी से लागू किया जाता है।

val f = (a: Int) => (b: Int) => (c: Int) => a + b + c
val g = f(1)(2)

यह मामूली सुविधा कभी-कभी सार्थक होती है। ध्यान दें कि फ़ंक्शंस टाइप पैरामीट्रिक नहीं हो सकते हैं, इसलिए कुछ मामलों में एक विधि की आवश्यकता होती है।

आपका दूसरा उदाहरण एक हाइब्रिड है: एक पैरामीटर पैरामीटर विधि जो एक फ़ंक्शन लौटाती है।

मल्टी स्टेज कम्प्यूटेशन

अन्य उपयोगी कार्य कहाँ हैं? यहाँ एक पैटर्न है जो हर समय आता है:

def v(t: Double, k: Double): Double = {
   // expensive computation based only on t
   val ft = f(t)

   g(ft, k)
}

v(1, 1); v(1, 2);

हम परिणाम कैसे साझा कर सकते हैं f(t)? इसका एक सामान्य समाधान एक सदिश संस्करण प्रदान करना है v:

def v(t: Double, ks: Seq[Double]: Seq[Double] = {
   val ft = f(t)
   ks map {k => g(ft, k)}
}

बदसूरत! हमने असंबंधित चिंताओं को उलझा दिया है - g(f(t), k)एक अनुक्रम में गणना और मानचित्रण ks

val v = { (t: Double) =>
   val ft = f(t)
   (k: Double) => g(ft, k)       
}
val t = 1
val ks = Seq(1, 2)
val vs = ks map (v(t))

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

def v(t:Double): Double => Double = {
   val ft = f(t)
   (k: Double) => g(ft, k)       
}

लेकिन अगर हम कई पैरामीटर सेक्शन वाली विधि के साथ भी ऐसा करने की कोशिश करते हैं, तो हम अटक जाते हैं:

def v(t: Double)(k: Double): Double = {
                ^
                `-- Can't insert computation here!
}

3
शानदार जवाब; काश मैं सिर्फ एक से अधिक upvotes था। मैं स्टाइल गाइड को पचाने और लागू करने जा रहा हूं; अगर मैं सफलतापूर्वक हूँ, यह चुना हुआ उत्तर है ...
davetron5000

1
आप अपने पाश उदाहरण को सही करना चाह सकते हैं:def loop[A](n: Int)(body: => A): Unit = (0 until n) foreach (n => body)
उमर वैन क्लोइटेन

यह संकलित नहीं करता है:val f: (a: Int) => (b: Int) => (c: Int) = a + b + c
nilskp

"एम तर्क अनुभागों के साथ विधि के लिए एन तर्क सूची का आवेदन, जहां एन <एम, एक फ़ंक्शन के साथ स्पष्ट रूप से एक _, या स्पष्ट रूप से एक अपेक्षित प्रकार के फ़ंक्शनएन [..] में परिवर्तित किया जा सकता है।" <br/> क्या यह फंक्शनएक्स [..] नहीं होना चाहिए, जहां एक्स = एमएन?
स्टैकओवरफ्लॉवर 14

"यह संकलित नहीं करता है: val f: (a: Int) => (b: Int) => (c: Int) = a + b + c" मुझे नहीं लगता "f: (a: Int) = > (b: Int) => (c: Int) "सही वाक्यविन्यास है। संभवत: प्रतिगामी का मतलब "f: Int => Int => Int => Int" है। क्योंकि => सही सहयोगी है, यह वास्तव में "f: Int => (Int => (Int => Int))" है। तो f (1) (2) टाइप का है Int => Int (जो कि f के टाइप में सबसे भीतरी है)
stackoverflower

16

आप केवल कार्य कर सकते हैं, विधियाँ नहीं। addएक विधि है, इसलिए आपको _किसी फ़ंक्शन में इसके रूपांतरण को बाध्य करने की आवश्यकता है । add2एक फ़ंक्शन देता है, इसलिए _न केवल अनावश्यक है, बल्कि यहां कोई मतलब नहीं है।

को ध्यान में रखते कैसे अलग अलग तरीकों और कार्यों (JVM के नजरिए से जैसे) कर रहे हैं, स्काला एक बहुत अच्छा काम उन दोनों के बीच लाइन धुंधला और ज्यादातर मामलों में "सही काम" कर रही है, लेकिन वहाँ है एक अंतर है, और कभी कभी तुम सिर्फ जरूरत इसके बारे में जानना।


यह समझ में आता है, तो आप फॉर्म डिफ ऐड (a: Int) (b: Int) को क्या कहते हैं? शब्द और वाक्यांश जोड़ने के बीच का अंतर बताने वाला शब्द / वाक्यांश क्या है: (इंट, बी: इंट)?
davetron5000

@ davetron5000 पहला पैरामीटर कई पैरामीटर सूचियों के साथ एक विधि है, दूसरा एक पैरामीटर सूची के साथ एक विधि है।
जेस्पर

5

मुझे लगता है कि यह अंतरों को समझने में मदद करता है अगर मैं इससे जोड़ता हूं कि def add(a: Int)(b: Int): Intआप के साथ बहुत ही दो तरीकों के साथ एक विधि को परिभाषित करें , केवल उन दो मापदंडों को दो पैरामीटर सूचियों में बांटा गया है (अन्य टिप्पणियों में इसके परिणाम देखें)। वास्तव में, यह विधि अभी int add(int a, int a)तक जावा (स्काला नहीं!) का संबंध है। जब आप लिखते हैं add(5)_, तो यह केवल एक फ़ंक्शन शाब्दिक, का एक छोटा रूप है { b: Int => add(1)(b) }। दूसरी ओर, add2(a: Int) = { b: Int => a + b }आप एक विधि को परिभाषित करते हैं जिसमें केवल एक पैरामीटर होता है, और जावा के लिए यह होगा scala.Function add2(int a)। जब आप add2(1)स्काला में लिखते हैं तो यह केवल एक सादा विधि कॉल होता है (जैसा कि किसी फ़ंक्शन शाब्दिक के विपरीत)।

यह भी ध्यान दें कि यदि आपके पास तुरंत सभी मापदंडों को प्रदान करने की addतुलना में (संभावित) कम ओवरहेड है add2। जैसे जेवीएम स्तर पर add(5)(6)सिर्फ अनुवाद किया जाता add(5, 6)है, कोई Functionवस्तु नहीं बनाई जाती है। दूसरी ओर, add2(5)(6)पहले एक ऐसी Functionवस्तु का निर्माण करेगा जो संलग्न है 5, और फिर apply(6)उस पर कॉल करें ।

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