करी या आंशिक अनुप्रयोग के बारे में क्या खास है?


9

मैं हर रोज फंक्शनल प्रोग्रामिंग पर लेख पढ़ रहा हूं और यथासंभव कुछ प्रथाओं को लागू करने की कोशिश कर रहा हूं। लेकिन मुझे समझ में नहीं आ रहा है कि करी या आंशिक अनुप्रयोग में क्या अद्वितीय है।

इस ग्रूवी कोड को एक उदाहरण के रूप में लें:

def mul = { a, b -> a * b }
def tripler1 = mul.curry(3)
def tripler2 = { mul(3, it) }

मुझे समझ नहीं आता के बीच अंतर है क्या tripler1और tripler2। क्या वे दोनों एक ही नहीं हैं? 'करी' का समर्थन शुद्ध या आंशिक कार्यात्मक भाषाओं जैसे ग्रूवी, स्काला, हास्केल आदि में किया जाता है, लेकिन मैं एक ही चीज़ बना सकता हूं (बाएं-करी, राइट-करी, एन-करी या आंशिक अनुप्रयोग) बस एक अन्य या अनाम बनाकर फ़ंक्शन या क्लोजर जो मूल फ़ंक्शन (जैसे tripler2) को अधिकांश भाषाओं (यहां तक ​​कि सी) के मापदंडों को आगे बढ़ाएगा ।

क्या मुझसे कोई चूक हो रही है? ऐसे स्थान हैं जहां मैं अपने ग्रेल्स एप्लिकेशन में करी और आंशिक अनुप्रयोग का उपयोग कर सकता हूं, लेकिन मैं ऐसा करने में संकोच कर रहा हूं क्योंकि मैं खुद से पूछ रहा हूं "यह कैसे अलग है?"

कृपया मुझे ज्ञान दो।

संपादित करें: क्या आप लोग कह रहे हैं कि आंशिक रूप से आवेदन / क्यूरिंग किसी अन्य फ़ंक्शन को बनाने / कॉल करने की तुलना में अधिक कुशल है जो मूल फ़ंक्शन के लिए डिफ़ॉल्ट पैरामीटर को अग्रेषित करता है?


1
क्या कोई कृपया "करी" या "करी" टैग बना सकता है?
विघ्नेश्वरन

आप सी में करी कैसे करते हैं?
जियोर्जियो

यह शायद आंशिक अनुप्रयोग प्रोग्रामर के
। stackexchange.com/questions/152868/…

1
@Vigneshwaran: AFAIK आपको क्यूरिंग का समर्थन करने वाली भाषा में एक और फ़ंक्शन बनाने की आवश्यकता नहीं है। उदाहरण के लिए, हास्केल में f x y = x + yइसका मतलब है कि fएक फ़ंक्शन है जो एक इंट पैरामीटर लेता है। f x(पर fलागू x) का परिणाम एक फ़ंक्शन है जो एक इंट पैरामीटर लेता है। परिणाम f x y(या (f x) y, यानी पर f xलागू y) एक अभिव्यक्ति है जो कोई इनपुट पैरामीटर नहीं लेता है और कम करके मूल्यांकन किया जाता है x + y
जियोर्जियो

1
आप समान चीज़ों को प्राप्त कर सकते हैं, लेकिन सी के माध्यम से आपके द्वारा किए जाने वाले प्रयास की मात्रा बहुत अधिक दर्दनाक है और
हस्केल

जवाबों:


8

करीकरण एक फ़ंक्शन को मोड़ने / प्रतिनिधित्व करने के बारे में है जो n इनपुटों को n फ़ंक्शन में लेता है जो प्रत्येक 1 इनपुट लेता है। आंशिक अनुप्रयोग किसी इनपुट के कुछ फ़ंक्शन को ठीक करने के बारे में है।

आंशिक आवेदन के लिए प्रेरणा मुख्य रूप से यह है कि यह उच्च आदेश फ़ंक्शन पुस्तकालयों को लिखना आसान बनाता है। उदाहरण के लिए C ++ STL में एल्गोरिदम सभी बड़े पैमाने पर विधेय या एकात्मक कार्य लेते हैं, bind1st पुस्तकालय उपयोगकर्ता को गैर-अनारी कार्यों में मान बाउंड के साथ हुक करने की अनुमति देता है। पुस्तकालय लेखक को बाइनरी संस्करणों को प्रदान करने के लिए एकात्मक कार्य करने वाले सभी एल्गोरिदम के लिए अतिभारित कार्य प्रदान करने की आवश्यकता नहीं होती है

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


क्या curryingभाषाओं के लिए ग्रूवी या लागू कुछ विशिष्ट है?
उभयचर

@ अपनी भाषा में कुछ ऐसा लागू होता है, जो विडंबना है, लेकिन विडंबना यह है कि करी वास्तव में इसे प्रोग्रामर
jk।

@jk। क्या आप यह कह रहे हैं कि किसी अन्य फ़ंक्शन को बनाने और कॉल करने की तुलना में करी / आंशिक अनुप्रयोग अधिक कुशल है?
विग्नेश्वरन

2
@ विग्नेश्वरन - यह आवश्यक रूप से अधिक प्रदर्शन करने वाला नहीं है, लेकिन प्रोग्रामर के समय के संदर्भ में यह निश्चित रूप से अधिक कुशल है। यह भी ध्यान दें कि क्यूरिंग कई कार्यात्मक भाषाओं द्वारा समर्थित है, लेकिन आमतौर पर OO या प्रक्रियात्मक भाषाओं में समर्थित नहीं है। (या कम से कम, भाषा से ही नहीं।)
स्टीफन C

6

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

और ऑप्टिमाइज़र उस पर गौर करेगा और तुरंत उस चीज़ पर जा सकता है जिसे वह समझ सकता है। करीना अंत उपयोगकर्ता के लिए एक अच्छी छोटी चाल है, लेकिन एक भाषा डिजाइन के दृष्टिकोण से बहुत बेहतर लाभ है। यह वास्तव में सभी तरीकों एकल के रूप में संभाल करने के लिए अच्छा A -> Bहै, जहां Bएक और तरीका हो सकता है।

यह सरल करता है कि उच्च क्रम के कार्यों को संभालने के लिए आपको किन तरीकों को लिखना है। भाषा में आपके स्थैतिक विश्लेषण और अनुकूलन के साथ काम करने का एक ही रास्ता है कि एक ज्ञात तरीके से व्यवहार किया जाए। पैरामीटर बाइंडिंग केवल इस सामान्य व्यवहार को करने के लिए हुप्स की आवश्यकता के बजाय डिजाइन से बाहर हो जाता है।


6

जैसा @ जक। कोडित, कोडिंग को अधिक सामान्य बनाने में मदद कर सकता है।

उदाहरण के लिए, मान लीजिए कि आपके पास ये तीन कार्य थे (हास्केल में):

> let q a b = (2 + a) * b

> let r g = g 3

> let f a b = b (a 1)

समारोह fयहाँ तर्क के रूप में दो कार्यों लेता है, गुजरता 1पहले कार्य करने के लिए और दूसरा कार्य करने के लिए पहली कॉल का परिणाम गुजरता है।

कॉल करने के लिए अगर हम थे fका उपयोग कर qऔर rतर्क के रूप में, यह प्रभावी रूप से कर रहा हूँ:

> r (q 1)

जहां qलागू किया जाएगा 1और एक और फ़ंक्शन वापस करना होगा (जैसा qकि करी है); इस लौटे फ़ंक्शन को तब rउसके तर्क के रूप में पारित किया जाएगा जिसे एक तर्क दिया जाएगा 3। इसका परिणाम एक मूल्य होगा 9

अब, मान लें कि हमारे दो अन्य कार्य हैं:

> let s a = 3 * a

> let t a = 4 + a

हम करने के लिए इन गुजर सकता है fके रूप में अच्छी तरह से और मान मिलता है 7या 15, पर कि क्या हमारे तर्कों थे निर्भर करता है s tया t s। चूंकि इन कार्यों दोनों एक फ़ंक्शन के बजाय कोई मान, कोई आंशिक आवेदन में जगह ले जाएगा f s tया f t s

अगर हमने लिखा fहै qऔर rमन में हमने आंशिक आवेदन के बजाय एक लंबोदर (अनाम फ़ंक्शन) का उपयोग किया है, उदाहरण के लिए:

> let f' a b = b (\x -> a 1 x)

लेकिन यह सामान्यता को प्रतिबंधित कर देता f'fतर्कों qऔर rया के साथ बुलाया जा सकता है sऔर t, लेकिन f'केवल के साथ बुलाया जा सकता है qऔर r- f' s tऔर f' t sदोनों में त्रुटि होती है।

अधिक

यदि / f'a जोड़ी के साथ बुलाया जाता है जहाँ दो से अधिक तर्क दिए गए हैं, तब भी अंत में आंशिक रूप से लागू किया जाएगा ।q'r'q'q'f'

वैकल्पिक रूप से, आप अंदर qके fबजाय बाहर लपेट सकते हैं , लेकिन यह आपको एक बुरा नेस्टेड लैम्ब्डा के साथ छोड़ देगा:

f (\x -> (\y -> q x y)) r

जो अनिवार्य रूप qसे पहली जगह में था!


तुमने मेरी आँखें खोल दीं। आपके जवाब ने मुझे एहसास दिलाया कि मूल फ़ंक्शन के लिए तर्कों को पारित करने वाले नए फ़ंक्शन को बनाने से आंशिक रूप से लागू किए गए कार्य कैसे भिन्न होते हैं। 1. करी / पाइड फ़ंक्शंस (जैसे f (q.curry (2)) के आस-पास से गुजरना, केवल एक अस्थायी उपयोग के लिए अनावश्यक रूप से अलग-अलग फ़ंक्शंस बनाने की तुलना में नट हैं। (कार्यात्मक भाषाओं जैसे ग्रूवी में)
विग्नेश्वरन

2. मेरे सवाल में, मैंने कहा, "मैं सी में भी ऐसा ही कर सकता हूं।" हाँ, पर गैर कार्यात्मक भाषाओं, आप डेटा के रूप में कार्य के आसपास पारित जहां नहीं कर सकते, एक अलग समारोह बनाना, जिनका मूल करने के लिए आगे पैरामीटर, currying / पा के सभी लाभ नहीं है में
Vigneshwaran

मैंने देखा कि ग्रूवी सामान्य प्रकार के हास्केल का समर्थन नहीं करता है। मैं लिखना पड़ा def f = { a, b -> b a.curry(1) }बनाने के लिए f q, rकाम करते हैं और करने के लिए def f = { a, b -> b a(1) }या def f = { a, b -> b a.curry(1)() }के लिए f s, tकाम करने के लिए। आपको सभी मापदंडों को पास करना होगा या स्पष्ट रूप से कहना होगा कि आप करी कर रहे हैं। :(
विग्नेश्वरन

2
@ विग्नेश्वरन: हां, यह कहना सुरक्षित है कि हास्केल और करी एक साथ बहुत अच्छी तरह से चलते हैं । ;] ध्यान दें कि हास्केल में, डिफ़ॉल्ट रूप से व्हाईटस्पेस द्वारा फ़ंक्शन को (सही परिभाषा में) फ़ंक्शन अनुप्रयोग को इंगित करता है, इसलिए f x yइसका अर्थ है कि कितनी भाषाएं लिखेंगे f(x)(y), नहीं f(x, y)। यदि आप लिखते हैं qतो शायद आपका कोड ग्रूवी में काम करेगा ताकि यह उम्मीद की जाए कि इसे कहा जाए q(1)(2)?
सीए मैक्कैन

1
@Vigneshwaran खुशी है कि मैं मदद कर सकता है! मुझे लगता है कि आप आंशिक आवेदन कर रहे हैं यह स्पष्ट रूप से कहने के बारे में आपका दर्द महसूस करता है। क्लॉजुरे में मुझे क्या करना है (partial f a b ...)- हास्केल के लिए इस्तेमाल किया जा रहा है, मैं अन्य भाषाओं में प्रोग्रामिंग करते समय उचित करी बहुत याद आती है (हालांकि मैं हाल ही में एफ # में काम कर रहा हूं जो, शुक्र है, इसका समर्थन करता है)।
पॉल

3

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

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

(:) :: a -> [a] -> [a]

एक ही तर्क ले; फ़ंक्शन प्रकार कंस्ट्रक्टर की संबद्धता के कारण ->, उपरोक्त इसके बराबर है:

(:) :: a -> ([a] -> [a])

जो एक फ़ंक्शन है जो एक फ़ंक्शन लेता है aऔर एक फ़ंक्शन देता है [a] -> [a]

यह हमें इस तरह के कार्य लिखने की अनुमति देता है:

($) :: (a -> b) -> a -> b

जो किसी भी फ़ंक्शन को उपयुक्त प्रकार के तर्क पर लागू कर सकता है । यहां तक ​​कि पागल जैसे:

f :: (t, t1) -> t -> t1 -> (t2 -> t3 -> (t, t1)) -> t2 -> t3 -> [(t, t1)]
f q r s t u v = q : (r, s) : [t u v]

f' :: () -> Char -> (t2 -> t3 -> ((), Char)) -> t2 -> t3 -> [((), Char)]
f' = f $ ((), 'a')  -- <== works fine

ठीक है, इसलिए यह एक आकस्मिक उदाहरण था। लेकिन एक अधिक उपयोगी में एप्‍लीकेटिव टाइप क्‍लास शामिल है, जिसमें यह विधि सम्‍मिलित है:

(<*>) :: Applicative f => f (a -> b) -> f a -> f b

जैसा कि आप देख सकते हैं, $यदि आप Applicative fबिट को हटाते हैं तो प्रकार समान होता है , और वास्तव में, यह वर्ग एक संदर्भ में फ़ंक्शन एप्लिकेशन का वर्णन करता है। इसलिए सामान्य फ़ंक्शन अनुप्रयोग के बजाय:

ghci> map (+3) [1..5]  
[4,5,6,7,8]

हम एक आवेदन के संदर्भ में कार्य कर सकते हैं; उदाहरण के लिए, शायद संदर्भ में जिसमें कुछ मौजूद हो सकता है या गायब हो सकता है:

ghci> Just map <*> Just (+3) <*> Just [1..5]
Just [4,5,6,7,8]

ghci> Just map <*> Nothing <*> Just [1..5]
Nothing

अब वास्तव में अच्छा हिस्सा यह है कि एपेक्टिव टाइप क्लास में एक से अधिक तर्कों के कार्यों के बारे में कुछ भी उल्लेख नहीं किया गया है - फिर भी, यह उनसे निपट सकता है, यहां तक ​​कि 6 तर्कों के कार्य भी f:

fA' :: Maybe (() -> Char -> (t2 -> t3 -> ((), Char)) -> t2 -> t3 -> [((), Char)])
fA' = Just f <*> Just ((), 'a')

जहाँ तक मुझे पता है, इसके सामान्य रूप में एपेक्टिव टाइप वर्ग आंशिक आवेदन के कुछ गर्भाधान के बिना संभव नहीं होगा। (वहाँ किसी भी प्रोग्रामिंग विशेषज्ञों के लिए - कृपया मुझे सही करें अगर मैं गलत हूँ!) बेशक, अगर आपकी भाषा में आंशिक अनुप्रयोग का अभाव है, तो आप इसे किसी भी रूप में बना सकते हैं , लेकिन ... यह सिर्फ एक ही नहीं है, क्या यह है ? :)


1
Applicativeबिना करी या आंशिक अनुप्रयोग का उपयोग करेगा fzip :: (f a, f b) -> f (a, b)। उच्च-क्रम वाले कार्यों की भाषा में, यह आपको फ़ंक्शंस के संदर्भ में करी और आंशिक अनुप्रयोग को उठाने देता है और इसके बराबर है (<*>)। उच्च-क्रम के कार्यों के बिना आपके पास fmapइतना सारा सामान बेकार नहीं होगा।
सीए मैक्कैन नोव

प्रतिक्रिया के लिए @CAMcCann धन्यवाद! मुझे पता था कि मैं इस जवाब के साथ मेरे सिर पर था। तो क्या मैंने गलत कहा है?

1
यह निश्चित रूप से आत्मा में सही है। "सामान्य रूप", "संभव" की परिभाषाओं पर बालों को विभाजित करना, और "आंशिक अनुप्रयोग की अवधारणा" होने से सरल तथ्य यह नहीं बदलेगा कि आकर्षक f <$> x <*> yमुहावरेदार शैली आसानी से काम करती है क्योंकि करी और आंशिक अनुप्रयोग आसानी से काम करते हैं। दूसरे शब्दों में, जो यहां संभव है उससे ज्यादा महत्वपूर्ण है सुखद
सीए मैककैन नोव

हर बार जब मैं कार्यात्मक प्रोग्रामिंग के कोड उदाहरण देखता हूं, तो मुझे यकीन है कि यह एक विस्तृत मजाक है और यह मौजूद नहीं है।
कीवेल्ली

1
@Kieveli यह दुर्भाग्यपूर्ण है कि आप ऐसा महसूस करते हैं। वहाँ कई ठीक ट्यूटोरियल वहाँ है कि आप को मिल जाएगा और मूल बातें की एक अच्छी समझ के साथ चल रहा है।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.