मुझे समझ नहीं आ रहा है कि "लिफ्टिंग" क्या है। क्या मुझे समझने से पहले भिक्षुओं को समझना चाहिए कि "लिफ्ट" क्या है? (मैं भिक्षुओं के बारे में पूरी तरह से अनभिज्ञ हूं, :) या कोई इसे सरल शब्दों में मुझे समझा सकता है?
मुझे समझ नहीं आ रहा है कि "लिफ्टिंग" क्या है। क्या मुझे समझने से पहले भिक्षुओं को समझना चाहिए कि "लिफ्ट" क्या है? (मैं भिक्षुओं के बारे में पूरी तरह से अनभिज्ञ हूं, :) या कोई इसे सरल शब्दों में मुझे समझा सकता है?
जवाबों:
लिफ्टिंग एक गणितीय अवधारणा की तुलना में एक डिजाइन पैटर्न से अधिक है (हालांकि मुझे उम्मीद है कि यहां आसपास कोई व्यक्ति अब मुझे दिखाएगा कि लिफ्ट कैसे एक श्रेणी या कुछ है)।
आमतौर पर आपके पास एक पैरामीटर के साथ कुछ डेटा प्रकार होते हैं। कुछ इस तरह
data Foo a = Foo { ...stuff here ...}
मान लें कि आप Foo
संख्यात्मक प्रकारों ( Int
, Double
आदि) के बहुत सारे उपयोग करते हैं और आप कोड लिखना जारी रखते हैं जो इन नंबरों को खोल देता है, उन्हें जोड़ता है या उन्हें गुणा करता है, और फिर उन्हें वापस लपेटता है। आप एक बार अनपरा-और-रैप कोड लिखकर इसे शॉर्ट-सर्किट कर सकते हैं। इस फ़ंक्शन को पारंपरिक रूप से "लिफ्ट" कहा जाता है क्योंकि यह इस तरह दिखता है:
liftFoo2 :: (a -> b -> c) -> Foo a -> Foo b -> Foo c
दूसरे शब्दों में, आपके पास एक फ़ंक्शन है जो दो-तर्क फ़ंक्शन (जैसे (+)
ऑपरेटर) लेता है और इसे फ़ॉइस के बराबर फ़ंक्शन में बदल देता है।
तो अब आप लिख सकते हैं
addFoo = liftFoo2 (+)
संपादित करें: अधिक जानकारी
आप निश्चित रूप से कर सकते हैं liftFoo3
, liftFoo4
और इसी तरह। हालांकि यह अक्सर आवश्यक नहीं होता है।
अवलोकन से शुरू करें
liftFoo1 :: (a -> b) -> Foo a -> Foo b
लेकिन यह बिल्कुल वैसा ही है fmap
। तो इसके बजाय liftFoo1
आप लिखेंगे
instance Functor Foo where
fmap f foo = ...
यदि आप वास्तव में पूर्ण नियमितता चाहते हैं तो आप कह सकते हैं
liftFoo1 = fmap
यदि आप Foo
एक फ़नकार में बना सकते हैं , तो शायद आप इसे एक फ़ंक्शनल फ़ंक्टर बना सकते हैं। वास्तव में, यदि आप लिख सकते हैं liftFoo2
तो आवेदन का उदाहरण इस तरह दिखता है:
import Control.Applicative
instance Applicative Foo where
pure x = Foo $ ... -- Wrap 'x' inside a Foo.
(<*>) = liftFoo2 ($)
(<*>)
फू के लिए ऑपरेटर प्रकार है
(<*>) :: Foo (a -> b) -> Foo a -> Foo b
यह लिपटे फ़ंक्शन को लिपटे मूल्य पर लागू करता है। इसलिए यदि आप कार्यान्वित कर सकते हैं liftFoo2
तो आप इसके संदर्भ में लिख सकते हैं। या आप इसे सीधे लागू कर सकते हैं और परेशान नहीं कर सकते liftFoo2
, क्योंकि Control.Applicative
मॉड्यूल शामिल है
liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
और इसी तरह देखते हैं liftA
और liftA3
। लेकिन आप वास्तव में उन्हें बहुत बार उपयोग नहीं करते हैं क्योंकि एक और ऑपरेटर है
(<$>) = fmap
यह आपको लिखने देता है:
result = myFunction <$> arg1 <*> arg2 <*> arg3 <*> arg4
शब्द myFunction <$> arg1
फू में लिपटे एक नया फ़ंक्शन देता है। यह बदले में अगले तर्क पर लागू किया जा सकता है (<*>)
, और इसी तरह। इसलिए अब हर ऐरिटी के लिए एक लिफ्ट फंक्शन होने के बजाय, आपके पास केवल ऐप्लिकेशंस की डेज़ी श्रृंखला है।
lift id == id
और lift (f . g) == (lift f) . (lift g)
।
id
और .
पहचान तीर और तीर कुछ श्रेणी की संरचना, क्रमशः रहे हैं। आमतौर पर हास्केल की बात करते समय, प्रश्न में श्रेणी "हस्क" है, जिसके तीर हास्केल फ़ंक्शन हैं (दूसरे शब्दों में, id
और .
हास्केल फ़ंक्शन जिसे आप जानते हैं और प्यार करते हैं) को देखें।
instance Functor Foo
नहीं instance Foo Functor
? मैं खुद को संपादित करूँगा लेकिन मुझे यकीन नहीं है कि 100%।
पॉल और यारचू दोनों अच्छे स्पष्टीकरण हैं।
मैं यह जोड़ना चाहता हूं कि उठाए जा रहे फ़ंक्शन में मनमानी संख्या हो सकती है और उन्हें एक ही प्रकार का नहीं होना चाहिए। उदाहरण के लिए, आप एक लिफ्ट भी परिभाषित कर सकते हैं 1:
liftFoo1 :: (a -> b) -> Foo a -> Foo b
सामान्य तौर पर, 1 तर्क लेने वाले फ़ंक्शंस की लिफ्टिंग को टाइप क्लास में कैप्चर किया जाता है, और उठाने की क्रिया Functor
को कहा जाता है fmap
:
fmap :: Functor f => (a -> b) -> f a -> f b
इसी liftFoo1
प्रकार के साथ समानता पर ध्यान दें । वास्तव में, यदि आपके पास है, तो आप इसका उदाहरण liftFoo1
बना सकते हैं :Foo
Functor
instance Functor Foo where
fmap = liftFoo1
इसके अलावा, मनमाने ढंग से तर्कों को उठाने के सामान्यीकरण को प्रयोजनीय शैली कहा जाता है । जब तक आप फ़ंक्शंस की निश्चित संख्या के साथ फ़ंक्शंस को पकड़ नहीं लेते, तब तक इसमें डाइविंग न करें। लेकिन जब आप करते हैं, तो सीखें कि एक हास्केल का इस पर एक अच्छा अध्याय है। Typeclassopedia का वर्णन करता है एक और अच्छा दस्तावेज है functor और अनुप्रयोगी (और साथ ही अन्य प्रकार की कक्षाएं, जो दस्तावेज़ में सही अध्याय करने के लिए नीचे स्क्रॉल)।
उम्मीद है की यह मदद करेगा!
आइए एक उदाहरण से शुरू करते हैं (स्पष्ट प्रस्तुति के लिए कुछ सफेद स्थान जोड़ा गया है):
> import Control.Applicative
> replicate 3 'a'
"aaa"
> :t replicate
replicate :: Int -> b -> [b]
> :t liftA2
liftA2 :: (Applicative f) => (a -> b -> c) -> (f a -> f b -> f c)
> :t liftA2 replicate
liftA2 replicate :: (Applicative f) => f Int -> f b -> f [b]
> (liftA2 replicate) [1,2,3] ['a','b','c']
["a","b","c","aa","bb","cc","aaa","bbb","ccc"]
> ['a','b','c']
"abc"
liftA2
सादे प्रकारों के एक फंक्शन को लिपटे हुए उसी प्रकार केApplicative
फ़ंक्शन में बदल देता है , जैसे कि सूची IO
, आदि।
एक और आम लिफ्ट lift
से है Control.Monad.Trans
। यह एक मठ के एक कार्रवाई के लिए एक बदले हुए मठ के एक कार्रवाई को बदल देता है।
सामान्य तौर पर, "उठाना" लिफ्टों एक समारोह / कार्रवाई एक "लिपटे" प्रकार में (ताकि मूल कार्य "wraps के अंतर्गत" काम करने के लिए हो जाता है)।
इसे समझने का सबसे अच्छा तरीका है, और भिक्षु आदि, और यह समझने के लिए कि वे क्यों उपयोगी हैं, संभवतः इसे कोड करना और उपयोग करना है। यदि आपके द्वारा पहले से कोडित कुछ भी है तो आपको संदेह है कि इससे लाभ हो सकता है (यानी यह उस कोड को छोटा कर देगा, आदि), बस इसे आज़माएं और आप आसानी से अवधारणा को समझ लेंगे।
भारोत्तोलन एक अवधारणा है जो आपको किसी फ़ंक्शन को दूसरे (आमतौर पर अधिक सामान्य) सेटिंग में एक संबंधित फ़ंक्शन में बदलने की अनुमति देता है
http://haskell.org/haskellwiki/Lifting पर एक नज़र डालें
इस चमकदार ट्यूटोरियल के अनुसार , एक फ़नकार कुछ कंटेनर है (जैसे Maybe<a>
, List<a>
या Tree<a>
जो किसी अन्य प्रकार के तत्वों को संग्रहीत कर सकता है a
)। मैंने जावा जेनरिक नोटेशन का उपयोग किया है, <a>
तत्व प्रकार के लिए a
और तत्वों को पेड़ पर जामुन के रूप में सोचने के लिए Tree<a>
। एक फ़ंक्शन है fmap
, जो एक तत्व रूपांतरण फ़ंक्शन, a->b
और कंटेनर लेता है functor<a>
। यह a->b
कंटेनर के हर तत्व पर प्रभावी रूप से इसे परिवर्तित करने पर लागू होता है functor<b>
। जब केवल पहला तर्क दिया जाता है a->b
, fmap
प्रतीक्षा करता है functor<a>
। यही है, a->b
अकेले आपूर्ति इस तत्व-स्तरीय फ़ंक्शन को फ़ंक्शन में बदल देती है functor<a> -> functor<b>
जो कंटेनरों पर काम करती है। इसे लिफ्टिंग कहा जाता हैसमारोह के। क्योंकि कंटेनर को एक फफूंदनाशक भी कहा जाता है , मोनडैड्स के बजाय फन उठाने वालों के लिए एक शर्त है। मोनाड्स उठाने के लिए "समानांतर" की तरह हैं। दोनों फनकार धारणा पर भरोसा करते हैं और करते हैं f<a> -> f<b>
। अंतर यह है कि उठाना a->b
रूपांतरण के लिए उपयोग करता है जबकि मोनाड को उपयोगकर्ता को परिभाषित करने की आवश्यकता होती है a -> f<b>
।
r
से एक प्रकार के कार्य (चलो c
विविधता के लिए उपयोग करें ), फंक्टर हैं। वे "किसी भी" शामिल नहीं है c
। इस उदाहरण में, FMAP, समारोह रचना है एक लेने a -> b
समारोह और एक r -> a
एक, आप एक नया, देने के लिए r -> b
कार्य करते हैं। फिर भी कोई कंटेनर नहीं। यदि मैं कर सकता हूं, तो मैं इसे अंतिम वाक्य के लिए फिर से चिह्नित करूंगा।
fmap
एक फ़ंक्शन है, और किसी भी चीज़ के लिए "प्रतीक्षा" नहीं करता है; "कंटेनर" एक फ़नकार है , उठाने का पूरा बिंदु है। इसके अलावा, मोनाड हैं, अगर कुछ भी, उठाने के लिए एक दोहरी विचार: एक मोनाड आपको कुछ सकारात्मक संख्याओं को उठाने की अनुमति देता है, जैसे कि यह केवल एक बार उठाया गया था - यह चपटा के रूप में जाना जाता है ।
To wait
, to expect
, to anticipate
समानार्थक शब्द हैं। "फ़ंक्शन प्रतीक्षा करता है" कहकर मेरा मतलब है "फ़ंक्शन प्रत्याशित"।
b = 5 : a
और f 0 = 55
f n = g n
, दोनों में "कंटेनर" छद्म-उत्परिवर्तन शामिल है। यह भी तथ्य यह है कि सूचियों को आम तौर पर पूरी तरह से मेमोरी में संग्रहीत किया जाता है जबकि कार्यों को आमतौर पर गणना के रूप में संग्रहीत किया जाता है। लेकिन मेमोइज़िंग / मोनॉर्फिक सूचियां जो कॉल के बीच संग्रहीत नहीं हैं, दोनों उस विचार से बकवास को तोड़ते हैं।