2011-06-09 8 views
11
import Control.Applicative 
import Control.Arrow 

filter ((&&) <$> (>2) <*> (<7)) [1..10] 
filter ((>2) &&& (<7) >>> uncurry (&&)) [1..10] 

दोनों एक ही परिणाम प्राप्त करते हैं! हालांकि, मेरे लिए समझना बहुत मुश्किल है। क्या कोई यहां विस्तार से समझा सकता है?क्या आप फोरम में कोड को समझाएंगे?

+0

ये उदाहरण कहां से आते हैं? –

+0

नियमित और infix आवेदन दोनों के लिए आवेदन वास्तव में अपने स्वयं के वाक्यविन्यास चीनी की जरूरत है। 'फ़िल्टर ((> 2) <(&&)> (<7)) [1..10] 'बहुत अच्छी तरह से पढ़ा जाएगा। –

+0

@ पेलोटॉम: हमेशा मुहावरे ब्रैकेट होते हैं। अब हमारे पास मोनद की समझ है, आवेदक समझ क्यों नहीं? :) –

उत्तर

23

चलिए दूसरे के साथ शुरू करते हैं, जो कि आसान है। हम निम्नलिखित प्रकार के साथ यहां दो रहस्यमय ऑपरेटरों है,:

(&&&) :: Arrow a => a b c -> a b c' -> a b (c, c') 
(>>>) :: Category cat => cat a b -> cat b c -> cat a c 

Arrow और Category प्रकार कक्षाएं चीजें हैं जो कार्यों की तरह व्यवहार करते है, जो निश्चित रूप से काम करता है खुद को भी शामिल है के बारे में ज्यादातर रहे हैं, और यहां दोनों मामलों सादे (->) हैं। तो, प्रकार पुनर्लेखन कि उपयोग करने के लिए:

(&&&) :: (b -> c) -> (b -> c') -> (b -> (c, c')) 
(>>>) :: (a -> b) -> (b -> c) -> (a -> c) 

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

तो, अभिव्यक्ति (>2) &&& (<7) एक एकल संख्या लेती है और तुलना के आधार पर Bool मानों की एक जोड़ी उत्पन्न करती है। इसके परिणामस्वरूप uncurry (&&) में खिलाया जाता है, जो सिर्फ Bool एस की एक जोड़ी लेता है और उन्हें एक साथ करता है। परिणामी भविष्यवाणी सामान्य रूप से सूची को फ़िल्टर करने के लिए उपयोग की जाती है।


पहला एक और अधिक गूढ़ है। हम निम्नलिखित प्रकार के साथ फिर से दो रहस्यमय ऑपरेटरों है,,:

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

, ध्यान से देखें कि इस मामले में (<$>) का दूसरा तर्क (>2) है, जो टाइप (Ord a, Num a) => a -> Bool है, जबकि (<$>) तर्क के प्रकार टाइप f a है। ये कैसे संगत हैं?

जवाब यह है कि, जैसा कि हम पहले प्रकार हस्ताक्षर में a और cat के लिए (->) स्थानापन्न सकता है बस के रूप में, हम (->) a Bool रूप a -> Bool के बारे में सोच सकते हैं, और f के लिए ((->) a) स्थानापन्न है। तो,, प्रकार पुनर्लेखन बजाय ((->) t) का उपयोग अन्य प्रकार चर a के साथ संघर्ष से बचने के लिए:

(<$>) :: (a -> b) -> ((->) t) a -> ((->) t) b 
(<*>) :: ((->) t) (a -> b) -> ((->) t) a -> ((->) t) b 
अब

, चीजों को सामान्य इन्फ़िक्स रूप में वापस डाल:

(<$>) :: (a -> b) -> (t -> a) -> (t -> b) 
(<*>) :: (t -> (a -> b)) -> (t -> a) -> (t -> b) 

पहले पता चला समारोह होने के लिए रचना, जैसा कि आप प्रकारों से देख सकते हैं। दूसरा अधिक जटिल है, लेकिन एक बार और प्रकार आपको बताएंगे कि आपको क्या चाहिए - इसमें एक सामान्य प्रकार के तर्क के साथ दो कार्य होते हैं, एक फ़ंक्शन का उत्पादन करते हैं, दूसरा फ़ंक्शन को पास करने के लिए तर्क उत्पन्न करता है। दूसरे शब्दों में, \f g x -> f x (g x) जैसे कुछ। (यह फ़ंक्शन संयोजक तर्क में भी जाना जाता है, जो कि तर्ककर्ता Haskell Curry द्वारा व्यापक रूप से खोजा गया विषय है, जिसका नाम कोई संदेह नहीं है कि अजीब परिचित लगता है!)

(<$>) के संयोजन और "फैली" की (<*>) तरह क्या (<$>) अकेले करता है, इस मामले में, एक आम तर्क प्रकार के साथ दो तर्क, दो कार्यों के साथ एक समारोह लेने बाद के दो के लिए एक एकल मूल्य लागू करने का अर्थ है, जो फिर दो परिणामों में पहला फ़ंक्शन लागू करना। तो ((&&) <$> (>2) <*> (<7)) x(&&) ((>2) x) ((<7) x) को सरल बनाता है, या सामान्य infix शैली, x > 2 && x < 7 का उपयोग करता है। पहले की तरह, यौगिक अभिव्यक्ति का उपयोग सूची को सामान्य तरीके से फ़िल्टर करने के लिए किया जाता है।


इसके अलावा, ध्यान दें कि जब दोनों कार्यों कुछ हद तक समझ से परे हैं, एक बार आप का इस्तेमाल किया ऑपरेटरों आदत हो, वे वास्तव में काफी पठनीय हो। एक यौगिक अभिव्यक्ति पर पहला सार तत्व एक ही तर्क के लिए कई चीजें कर रहा है, जबकि दूसरा कार्य संरचना के साथ चीजों को स्ट्रिंग करने की मानक "पाइपलाइन" शैली का सामान्यीकृत रूप है।

व्यक्तिगत रूप से मुझे वास्तव में पहली बार पूरी तरह से पठनीय लगता है। लेकिन मुझे उम्मीद नहीं है कि ज्यादातर लोग सहमत होंगे!

+0

आपके उत्तर के लिए धन्यवाद! मैं मुझे बहुत स्पष्ट करता हूँ! –

+0

+1: आपने मेरे कोड को जितना संभव हो उतना बेहतर समझाया। – Landei

+3

मैं जोड़ता हूं कि हास्केलर्स हर बार अपने सिर में प्रदर्शित जिमनास्टिक टाइप नहीं करते हैं। इसके बजाए, पैटर्न: 'एफ <$> x <*> y' (वर्तनी: 'लिफ्टए 2 एफ एक्स वाई') अच्छी तरह से जाना जाता है। इसका अर्थ है: दो आवेदक-लिपटे मानों पर फ़ंक्शन 'f' लागू करें। कार्यों को "लिपटे मान" के रूप में देखा जा सकता है: '(> 2)' और '(<7) 'को लपेटा हुआ बूल के रूप में देखा जा सकता है, जहां' नम ए => ((->) ए) '' रैपर" है। फ़ंक्शन '(&&)' को परिणामस्वरूप लपेटा हुआ बूल उत्पन्न करने के लिए दो "लिपटे" बूल पर लागू किया जा सकता है। परिभाषित करने के लिए भी इस्तेमाल किया जा सकता है: 'avg = liftA2 (/) sum length' – Peaker

संबंधित मुद्दे