आंशिक रूप से लागू किए गए कार्यों के लिए स्केल करी बनाम


82

मुझे पता है के बारे में यहाँ पर कई सवाल देखते हैं कि क्या currying और आंशिक रूप से लागू किया कार्य हैं, लेकिन मैं कैसे वे अलग हैं के बारे में पूछ रहा हूँ। एक सरल उदाहरण के रूप में, यहां तक ​​कि संख्याओं को खोजने के लिए एक करीबी फ़ंक्शन है:

def filter(xs: List[Int], p: Int => Boolean): List[Int] =
   if (xs.isEmpty) xs
   else if (p(xs.head)) xs.head :: filter(xs.tail, p)
   else filter(xs.tail, p)

def modN(n: Int)(x: Int) = ((x % n) == 0)

तो आप इसे उपयोग करने के लिए निम्नलिखित लिख सकते हैं:

val nums = List(1,2,3,4,5,6,7,8)
println(filter(nums, modN(2))

जो रिटर्न: List(2,4,6,8)। लेकिन मैंने पाया है कि मैं इस तरह से एक ही काम कर सकता हूं:

def modN(n: Int, x: Int) = ((x % n) == 0)

val p = modN(2, _: Int)
println(filter(nums, p))

जो भी देता है: List(2,4,6,8)

तो मेरा सवाल यह है कि दोनों के बीच मुख्य अंतर क्या है, और आप एक दूसरे का उपयोग कब करेंगे? क्या यह उदाहरण दिखाने के लिए बहुत सरल है कि एक को दूसरे पर क्यों इस्तेमाल किया जाएगा?


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

1
इस पर एक नज़र डालें: stackoverflow.com/questions/8063325/…
Utaal

मुझे यह स्पष्टीकरण बहुत उपयोगी लगा, वह आंशिक कार्यों, आंशिक रूप से लागू किए गए कार्यों और करीने के बारे में बताते हैं, सभी एक पोस्ट में: stackoverflow.com/a/8650639/1287554
प्लास्टर ग्रोव

बेहतरीन लिंक, @PlastyGrove धन्यवाद!
एरिक

और आपके लिंक के लिए @Utaal का शुक्रिया। मार्टिन ओडस्की के किसी भी जवाब का बहुत महत्व है। मुझे लगता है कि ये अवधारणाएं अब क्लिक करना शुरू कर रही हैं।
एरिक

जवाबों:


88

प्लास्ट ग्रोव द्वारा जुड़े उत्तर में शब्दार्थ अंतर को काफी अच्छी तरह से समझाया गया है ।

कार्यक्षमता के संदर्भ में, हालांकि इसमें बहुत अधिक अंतर नहीं है। आइए इसे सत्यापित करने के लिए कुछ उदाहरण देखें। सबसे पहले, एक सामान्य कार्य:

scala> def modN(n: Int, x: Int): Boolean = ((x % n) == 0)
scala> modN(5, _ : Int)
res0: Int => Boolean = <function1>

इसलिए हमें एक आंशिक रूप से लागू होता है <function1>जो एक लेता है Int, क्योंकि हमने पहले ही इसे पूर्णांक दिया है। अब तक सब ठीक है। अब करी करने के लिए:

scala> def modNCurried(n: Int)(x: Int): Boolean = ((x % n) == 0)

इस अंकन के साथ, आप निम्न कार्य करने की उम्मीद करेंगे:

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

तो कई पैरामीटर सूची संकेतन वास्तव में एक करी फ़ंक्शन को तुरंत बनाने के लिए प्रतीत नहीं होता है (अनावश्यक ओवरहेड से बचने के लिए), लेकिन आपके लिए यह स्पष्ट रूप से बताने की प्रतीक्षा करता है कि आप इसे चाहते हैं (नोटेशन के कुछ अन्य फायदे भी हैं):

scala> modNCurried(5) _
res24: Int => Boolean = <function1>

जो ठीक वैसा ही है जो हमें पहले मिला था, इसलिए यहाँ कोई अंतर नहीं है, सिवाय संकेतन के। एक और उदाहरण:

scala> modN _
res35: (Int, Int) => Boolean = <function2>

scala> modNCurried _
res36: Int => (Int => Boolean) = <function1>

यह प्रदर्शित करता है कि एक "सामान्य" फ़ंक्शन को आंशिक रूप से लागू करने का कार्य एक फ़ंक्शन में होता है जो सभी मापदंडों को लेता है, जबकि आंशिक रूप से कई पैरामीटर सूचियों के साथ एक फ़ंक्शन लागू करने से फ़ंक्शन की एक श्रृंखला बनती है, प्रति पैरामीटर सूची में से एक, जो सभी एक नया फ़ंक्शन लौटाते हैं:

scala> def foo(a:Int, b:Int)(x:Int)(y:Int): Int = a * b + x - y
scala> foo _
res42: (Int, Int) => Int => (Int => Int) = <function2>

scala> res42(5)
<console>:10: error: not enough arguments for method apply: (v1: Int, v2: Int)Int => (Int => Int) in trait Function2.
Unspecified value parameter v2.

जैसा कि आप देख सकते हैं, क्योंकि पहले पैरामीटर की सूची fooमें दो पैरामीटर हैं, क्यूरी श्रृंखला में पहले फ़ंक्शन के दो पैरामीटर हैं।


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

scala> (modN _).curried
res45: Int => (Int => Boolean) = <function1

scala> modNCurried _
res46: Int => (Int => Boolean) = <function1>

स्क्रिप्टम के बाद

नोट: कारण कि आपका उदाहरण println(filter(nums, modN(2))बिना अंडरस्कोर के काम करता है, modN(2)ऐसा लगता है कि स्काला संकलक प्रोग्रामर के लिए एक सुविधा के रूप में उस अंडरस्कोर को मानता है।


इसके अलावा: जैसा कि @asflierl ने सही ढंग से बताया है, स्काला को उस प्रकार का अनुमान लगाने में सक्षम नहीं लगता जब आंशिक रूप से "सामान्य" कार्यों को लागू किया जाता है:

scala> modN(5, _)
<console>:9: error: missing parameter type for expanded function ((x$1) => modN(5, x$1))

जबकि यह जानकारी कई पैरामीटर सूची संकेतन का उपयोग करते हुए लिखे गए कार्यों के लिए उपलब्ध है:

scala> modNCurried(5) _
res3: Int => Boolean = <function1>

यह उत्तर दिखाता है कि यह कैसे बहुत उपयोगी हो सकता है।


एक महत्वपूर्ण अंतर यह है कि प्रकार का अनुमान अलग तरीके से काम करता है: gist.github.com/4529020

धन्यवाद, मैंने आपकी टिप्पणी के बारे में एक टिप्पणी जोड़ी :)
fresskoma

19

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

curry :: ((a, b) -> c) -> a -> b -> c 
   -- curry converts a function that takes all args in a tuple
   -- into one that takes separate arguments

uncurry :: (a -> b -> c) -> (a, b) -> c
   -- uncurry converts a function of separate args into a function on pairs.

आंशिक आवेदन कुछ तर्कों के लिए एक फ़ंक्शन लागू करने की क्षमता है , शेष तर्कों के लिए एक नया फ़ंक्शन उपज

यह याद रखना आसान है कि क्या आपको लगता है कि केवल करी करना ट्यूपल्स के साथ करना है।

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


में पूरी तरह से सहमत हूँ। शब्द के मूल अर्थ में स्केल शब्दों में "करी" शब्द कई पैरामीटर सूचियों के साथ एक फ़ंक्शन को एक पैरामीटर सूची के साथ एक फ़ंक्शन में बदलने की "प्रक्रिया" है। स्काला में इस परिवर्तन को ".curried" का उपयोग करके निष्पादित किया जा सकता है। दुर्भाग्य से, ऐसा लगता है कि स्काला शब्द के अर्थ को थोड़ा-बहुत अधिभारित कर दिया है, क्योंकि मूल रूप से इसे ".curried" के बजाय ".curry" कहा जाएगा।
फतह कोस्कुन

2

बहुक्रिया समारोह:

def modN(n: Int, x: Int) = ((x % n) == 0)

करी (या करी समारोह):

def modNCurried(n: Int)(x: Int) = ((x % n) == 0)

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


0

बस अंतिम बिंदु पर स्पष्ट करने के लिए

परिवर्धन: जैसा कि @asflierl ने सही ढंग से कहा है, स्काला को उस प्रकार का अनुमान लगाने में सक्षम नहीं लगता जब आंशिक रूप से "सामान्य" फ़ंक्शन लागू होते हैं:

अगर सभी पैरामीटर वाइल्डकार्ड हैं तो स्लैला टाइप कर सकता है, लेकिन जब उनमें से कुछ निर्दिष्ट हैं और उनमें से कुछ नहीं हैं।

scala> modN(_,_)
res38: (Int, Int) => Boolean = <function2>

scala> modN(1,_)
<console>:13: error: missing parameter type for expanded function ((x$1) => modN(1, x$1))
       modN(1,_)
              ^

0

मुझे अब तक का सबसे अच्छा विवरण: https://dzone.com/articles/difference-between-currying-amp-partially-applied

करी: एकल-तर्क कार्यों की एक श्रृंखला में कई तर्कों के साथ कार्यों का अपघटन। ध्यान दें, कि स्कैला किसी फ़ंक्शन को दूसरे फ़ंक्शन के तर्क के रूप में पारित करने की अनुमति देता है।

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

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