हास्केल में "उठाने" क्या है?


138

मुझे समझ नहीं आ रहा है कि "लिफ्टिंग" क्या है। क्या मुझे समझने से पहले भिक्षुओं को समझना चाहिए कि "लिफ्ट" क्या है? (मैं भिक्षुओं के बारे में पूरी तरह से अनभिज्ञ हूं, :) या कोई इसे सरल शब्दों में मुझे समझा सकता है?


9
शायद उपयोगी है, शायद नहीं: haskell.org/haskellwiki/ स्थानांतरण
kennytm

जवाबों:


179

लिफ्टिंग एक गणितीय अवधारणा की तुलना में एक डिजाइन पैटर्न से अधिक है (हालांकि मुझे उम्मीद है कि यहां आसपास कोई व्यक्ति अब मुझे दिखाएगा कि लिफ्ट कैसे एक श्रेणी या कुछ है)।

आमतौर पर आपके पास एक पैरामीटर के साथ कुछ डेटा प्रकार होते हैं। कुछ इस तरह

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फू में लिपटे एक नया फ़ंक्शन देता है। यह बदले में अगले तर्क पर लागू किया जा सकता है (<*>), और इसी तरह। इसलिए अब हर ऐरिटी के लिए एक लिफ्ट फंक्शन होने के बजाय, आपके पास केवल ऐप्लिकेशंस की डेज़ी श्रृंखला है।


26
यह शायद लायक याद दिलाया कि लिफ्टों मानक कानूनों का सम्मान करना चाहिए lift id == idऔर lift (f . g) == (lift f) . (lift g)
कार्लोस शेहिडगर

13
लिफ्ट्स वास्तव में "एक श्रेणी या कुछ और" हैं। कार्लोस सिर्फ functor कानूनों सूचीबद्ध है, जहां है idऔर .पहचान तीर और तीर कुछ श्रेणी की संरचना, क्रमशः रहे हैं। आमतौर पर हास्केल की बात करते समय, प्रश्न में श्रेणी "हस्क" है, जिसके तीर हास्केल फ़ंक्शन हैं (दूसरे शब्दों में, idऔर .हास्केल फ़ंक्शन जिसे आप जानते हैं और प्यार करते हैं) को देखें।
डैन बर्टन

3
यह पढ़ना चाहिए , ठीक instance Functor Fooनहीं instance Foo Functor? मैं खुद को संपादित करूँगा लेकिन मुझे यकीन नहीं है कि 100%।
अमलियॉ

2
एक आवेदन के बिना भारोत्तोलक = Functor है। मेरा मतलब है कि आपके पास 2 विकल्प हैं: फ़नकार या एपेक्टिव फ़ंक्टर। पहला लिफ्ट एकल पैरामीटर दूसरा मल्टी पैरामीटर फ़ंक्शन करता है। बहुत सुंदर है। सही? यह रॉकेट साइंस नहीं है :) यह सिर्फ ऐसा लगता है। महान जवाब btw के लिए धन्यवाद!
jhegedus

2
@atc: यह आंशिक अनुप्रयोग है। देखें wiki.haskell.org/Partial_application
पॉल जॉनसन

41

पॉल और यारचू दोनों अच्छे स्पष्टीकरण हैं।

मैं यह जोड़ना चाहता हूं कि उठाए जा रहे फ़ंक्शन में मनमानी संख्या हो सकती है और उन्हें एक ही प्रकार का नहीं होना चाहिए। उदाहरण के लिए, आप एक लिफ्ट भी परिभाषित कर सकते हैं 1:

liftFoo1 :: (a -> b) -> Foo a -> Foo b

सामान्य तौर पर, 1 तर्क लेने वाले फ़ंक्शंस की लिफ्टिंग को टाइप क्लास में कैप्चर किया जाता है, और उठाने की क्रिया Functorको कहा जाता है fmap:

fmap :: Functor f => (a -> b) -> f a -> f b

इसी liftFoo1प्रकार के साथ समानता पर ध्यान दें । वास्तव में, यदि आपके पास है, तो आप इसका उदाहरण liftFoo1बना सकते हैं :FooFunctor

instance Functor Foo where
  fmap = liftFoo1

इसके अलावा, मनमाने ढंग से तर्कों को उठाने के सामान्यीकरण को प्रयोजनीय शैली कहा जाता है । जब तक आप फ़ंक्शंस की निश्चित संख्या के साथ फ़ंक्शंस को पकड़ नहीं लेते, तब तक इसमें डाइविंग न करें। लेकिन जब आप करते हैं, तो सीखें कि एक हास्केल का इस पर एक अच्छा अध्याय है। Typeclassopedia का वर्णन करता है एक और अच्छा दस्तावेज है functor और अनुप्रयोगी (और साथ ही अन्य प्रकार की कक्षाएं, जो दस्तावेज़ में सही अध्याय करने के लिए नीचे स्क्रॉल)।

उम्मीद है की यह मदद करेगा!


25

आइए एक उदाहरण से शुरू करते हैं (स्पष्ट प्रस्तुति के लिए कुछ सफेद स्थान जोड़ा गया है):

> 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 के अंतर्गत" काम करने के लिए हो जाता है)।

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


13

भारोत्तोलन एक अवधारणा है जो आपको किसी फ़ंक्शन को दूसरे (आमतौर पर अधिक सामान्य) सेटिंग में एक संबंधित फ़ंक्शन में बदलने की अनुमति देता है

http://haskell.org/haskellwiki/Lifting पर एक नज़र डालें


40
हाँ, लेकिन यह पृष्ठ शुरू होता है "हम आम तौर पर एक (सहसंयोजक) फ़नकार के साथ शुरू करते हैं ..."। बिल्कुल नौसिखिया मित्र नहीं।
पॉल जॉनसन

3
लेकिन "फ़ंक्टर" जुड़ा हुआ है, इसलिए नौसिखिया केवल यह क्लिक कर सकता है कि फ़नकार क्या है। बेशक, लिंक किया गया पृष्ठ इतना अच्छा नहीं है। मुझे एक खाता प्राप्त करने और उसे ठीक करने की आवश्यकता है।
जॉकवे

10
यह एक समस्या है जो मैंने अन्य कार्यात्मक प्रोग्रामिंग साइटों पर देखी है; प्रत्येक अवधारणा को अन्य (अपरिचित) अवधारणाओं के संदर्भ में समझाया जाता है जब तक कि नौसिखिया पूर्ण चक्र (और मोड़ को गोल) नहीं कर देता। पुनरावृत्ति पसंद करने के साथ कुछ करना चाहिए।
डीएनए

2
इस लिंक के लिए वोट करें लिफ्ट एक दुनिया और दूसरी दुनिया के बीच संबंध बनाती है।
एक्स्टार्टअप

3
इस तरह के उत्तर केवल अच्छे हैं जब आप पहले से ही विषय को समझते हैं।
डबल ऑर्ट

-2

इस चमकदार ट्यूटोरियल के अनुसार , एक फ़नकार कुछ कंटेनर है (जैसे 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>


5
मैंने आपको एक निशान दिया, क्योंकि "एक फ़नकार कुछ कंटेनर है" ट्रोल-फ्लेवर्ड फ्लेम-बैट है। उदाहरण: कुछ rसे एक प्रकार के कार्य (चलो cविविधता के लिए उपयोग करें ), फंक्टर हैं। वे "किसी भी" शामिल नहीं है c। इस उदाहरण में, FMAP, समारोह रचना है एक लेने a -> bसमारोह और एक r -> aएक, आप एक नया, देने के लिए r -> bकार्य करते हैं। फिर भी कोई कंटेनर नहीं। यदि मैं कर सकता हूं, तो मैं इसे अंतिम वाक्य के लिए फिर से चिह्नित करूंगा।
बीएमपीएच

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

1
@BMeph To wait, to expect, to anticipateसमानार्थक शब्द हैं। "फ़ंक्शन प्रतीक्षा करता है" कहकर मेरा मतलब है "फ़ंक्शन प्रत्याशित"।
वैल

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

@Bepep केवल एक कारण सूची के बारे में सोचा जाता है कि एक कंटेनर की तरह अधिक है कि कई भाषाओं में उन्हें उत्परिवर्तित किया जा सकता है, जबकि पारंपरिक रूप से कार्य नहीं कर सकते हैं। लेकिन हास्केल में भी यह उचित बयान नहीं है कि न तो उत्परिवर्तित किया जा सकता है, और दोनों को कॉपी-म्यूट किया जा सकता है: b = 5 : aऔर f 0 = 55 f n = g n, दोनों में "कंटेनर" छद्म-उत्परिवर्तन शामिल है। यह भी तथ्य यह है कि सूचियों को आम तौर पर पूरी तरह से मेमोरी में संग्रहीत किया जाता है जबकि कार्यों को आमतौर पर गणना के रूप में संग्रहीत किया जाता है। लेकिन मेमोइज़िंग / मोनॉर्फिक सूचियां जो कॉल के बीच संग्रहीत नहीं हैं, दोनों उस विचार से बकवास को तोड़ते हैं।
अर्धविराम
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.