43

मैंने (f .) . g पैटर्न के अनुसार कई कार्यों को परिभाषित किया है। उदाहरण के लिए:क्या करता है (एफ।)। हास्केल में जी मतलब है?

countWhere = (length .) . filter 
duplicate = (concat .) . replicate 
concatMap = (concat .) . map 

इसका क्या अर्थ है?

+4

(च।)।जी मूल लेखक के कोड पर अच्छी तरह से छिपी हुई राय का भी हिस्सा हो सकता है। – Marton

+1

मुझे सच में यकीन नहीं है कि इसका क्या अर्थ है। –

+0

इसका मतलब है कि लेखक चालाक और अपठनीय कोड लिखने के लिए समाप्त हो रहा था। ;) – tibbe

उत्तर

82

डॉट ऑपरेटर (यानी (.)) function composition ऑपरेटर है।

infixr 9 . 
(.) :: (b -> c) -> (a -> b) -> a -> c 
f . g = \x -> f (g x) 

आप इसे देख सकते हैं प्रकार b -> c के एक समारोह और प्रकार a -> b का एक और समारोह लेता है और यानी प्रकार a -> c के एक समारोह (पहली से पीछे नहीं फ़ंक्शन के परिणाम पर लागू होता है जो देता है: यह इस प्रकार परिभाषित किया गया है समारोह)।

फ़ंक्शन संरचना ऑपरेटर बहुत उपयोगी है। यह आपको एक फ़ंक्शन के आउटपुट को किसी अन्य फ़ंक्शन के इनपुट में पाइप करने की अनुमति देता है। उदाहरण के लिए आप हास्केल में एक tac प्रोग्राम लिखने के रूप में निम्नानुसार सकता है:

main = interact (\x -> unlines (reverse (lines x))) 

नहीं बहुत पठनीय। समारोह रचना का उपयोग करना भले ही आप भुगतान लिख सकता है इस प्रकार है:

main = interact (unlines . reverse . lines) 

आप समारोह रचना देख सकते हैं बहुत उपयोगी है लेकिन आप इसे हर जगह उपयोग नहीं कर सकते। उदाहरण के लिए आप कर सकते हैं नहीं पाइप समारोह रचना का उपयोग कर length में filter के उत्पादन:

countWhere = length . filter -- this is not allowed 

कारण यह अनुमति नहीं है क्योंकि filter प्रकार (a -> Bool) -> [a] -> [a] की है। a -> b के साथ तुलना करें हम पाते हैं कि a(a -> Bool) और b प्रकार [a] -> [a] प्रकार है। इसके परिणामस्वरूप एक प्रकार का मिलान नहीं होता है क्योंकि हास्केल length को b -> c (यानी ([a] -> [a]) -> c) के प्रकार की अपेक्षा करता है। हालांकि यह वास्तव में [a] -> Int प्रकार है।

समाधान बहुत आसान है:

countWhere f = length . filter f 

हालांकि कुछ लोगों कि अतिरिक्त झूलने f पसंद नहीं है।

countWhere = (length .) . filter 

वे इस कैसे मिलता है: वे pointfree शैली में countWhere लिखने के लिए इस प्रकार पसंद करते हैं? पर विचार करें:

countWhere f xs = length (filter f xs) 

-- But `f x y` is `(f x) y`. Hence: 

countWhere f xs = length ((filter f) xs) 

-- But `\x -> f (g x)` is `f . g`. Hence: 

countWhere f = length . (filter f) 

-- But `f . g` is `(f .) g`. Hence: 

countWhere f = (length .) (filter f) 

-- But `\x -> f (g x)` is `f . g`. Hence: 

countWhere = (length .) . filter 

आप (f .) . g देख सकते हैं बस \x y -> f (g x y) है। इस अवधारणा को वास्तव में पुनरावृत्त किया जा सकता है:

f . g    --> \x -> f (g x) 
(f .) . g   --> \x y -> f (g x y) 
((f .) .) . g  --> \x y z -> f (g x y z) 
(((f .) .) .) . g --> \w x y z -> f (g w x y z) 

यह सुंदर नहीं है लेकिन यह काम पूरा हो जाता है।आप countWhere लिख सकता है के रूप में इस प्रकार के बजाय (.:) ऑपरेटर का उपयोग करना

f .: g = (f .) . g 
f .:: g = ((f .) .) . g 
f .::: g = (((f .) .) .) . g 

: दो कार्यों को देखते हुए आप अपने खुद के समारोह रचना ऑपरेटरों लिख सकते हैं दिलचस्प बात यह है

countWhere = length .: filter 

ही आप के रूप में बिंदु मुक्त शैली में (.:) लिख सकता है अच्छी तरह से:

f .: g = (f .) . g 

-- But `f . g` is `(.) f g`. Hence: 

f .: g = (.) (f .) g 

-- But `\x -> f x` is `f`. Hence: 

(f .:) = (.) (f .) 

-- But `(f .)` is `((.) f)`. Hence: 

(f .:) = (.) ((.) f) 

-- But `\x -> f (g x)` is `f . g`. Hence: 

(.:) = (.) . (.) 

इसी प्रकार हम पाते हैं:

(.::) = (.) . (.) . (.) 
(.:::) = (.) . (.) . (.) . (.) 

आप देख सकते हैं (.:), (.::) और (.:::) सिर्फ (.) की शक्तियों हों (जैसे कि वे (.) हैं)। गणित में संख्या के लिए:

x^0 = 1 
x^n = x * x^(n - 1) 
इसी प्रकार के कार्यों के लिए गणित में

:

f .^ 0 = id 
f .^ n = f . (f .^ (n - 1)) 

तो f(.) तो है:

(.) .^ 1 = (.) 
(.) .^ 2 = (.:) 
(.) .^ 3 = (.::) 
(.) .^ 4 = (.:::) 

हमें इस लेख के अंत के करीब लाता है यही कारण है कि। एक अंतिम चुनौती के लिए चलो pointfree शैली में निम्नलिखित समारोह लिखें:

mf a b c = filter a (map b c) 

mf a b c = filter a ((map b) c) 

mf a b = filter a . (map b) 

mf a b = (filter a .) (map b) 

mf a = (filter a .) . map 

mf a = (. map) (filter a .) 

mf a = (. map) ((filter a) .) 

mf a = (. map) ((.) (filter a)) 

mf a = ((. map) . (.)) (filter a) 

mf = ((. map) . (.)) . filter 

mf = (. map) . (.) . filter 

हम आगे इस सरल बना सकते हैं इस प्रकार है:

mf = compose map filter 
:

compose f g = (. f) . (.) . g 

compose f g = ((. f) . (.)) . g 

compose f g = (.) ((. f) . (.)) g 

compose f = (.) ((. f) . (.)) 

compose f = (.) ((. (.)) (. f)) 

compose f = ((.) . (. (.))) (. f) 

compose f = ((.) . (. (.))) (flip (.) f) 

compose f = ((.) . (. (.))) ((flip (.)) f) 

compose = ((.) . (. (.))) . (flip (.)) 

compose का उपयोग करके आप अब के रूप में mf लिख सकते हैं

हां यह थोड़ा बदसूरत है लेकिन यह भी वास्तव में एक भयानक दिमाग-दबाने वाली अवधारणा है। अब आप \x y z -> f x (g y z) फॉर्म compose f g के रूप में कोई भी फ़ंक्शन लिख सकते हैं और यह बहुत साफ है।

+2

फॉर्म का अभिव्यक्ति '(।)^I' अच्छी तरह से टाइप नहीं किया गया है, इसलिए वे वास्तव में हास्केल मान्य नहीं हैं। –

+1

सच है। हालांकि मैंने लिखा "इसी प्रकार गणित में कार्यों के लिए:" _ और चूंकि यह एक गणितीय स्पष्टीकरण है, मुझे लगता है कि संख्याओं के बजाय कार्यों के लिए '^' का उपयोग करना ठीक है। फिर भी मैं दोनों के बीच अंतर करने के लिए ऑपरेटर को '। ^' में बदल दूंगा। –

+0

मैं गणित में भी '(।)^I' को देखकर आश्चर्यचकित हूं। शायद ऐसी चीज के लिए एक औपचारिक ढांचा निर्भर प्रकार सिद्धांत में मौजूद है। यह दिलचस्प होगा। –

11

यह स्वाद का विषय है, लेकिन मुझे ऐसी शैली अप्रिय होने लगती है। सबसे पहले मैं इसका अर्थ बताऊंगा, और फिर मैं एक विकल्प सुझाता हूं जिसे मैं पसंद करता हूं।

किसी भी ऑपरेटर ? के लिए आपको (f . g) x = f (g x) और (f ?) x = f ? x पता होना चाहिए। इस से हम मान सकते हैं कि

countWhere p = ((length .) . filter) p 
       = (length .) (filter p) 
       = length . filter p 

तो

countWhere p xs = length (filter p xs) 

मैं एक समारोह का उपयोग करना पसंद .:

(.:) :: (r -> z) -> (a -> b -> r) -> a -> b -> z 
(f .: g) x y = f (g x y) 

फिर countWhere = length .: filter कहा जाता है। व्यक्तिगत रूप से मुझे यह बहुत स्पष्ट लगता है।

(.:Data.Composition और शायद अन्य स्थानों में भी परिभाषित किया गया है।)

+0

आप '(। :)' को '(। :) = fmap fmap fmap' के रूप में भी परिभाषित कर सकते हैं। यह अधिक सामान्य है क्योंकि आप इसे किसी भी मजेदार के लिए उपयोग कर सकते हैं। उदाहरण के लिए आप '(* 2) कर सकते हैं।: बस [1..5]'। बेशक आपको इसे '(। :) :: (फंक्शन एफ, फंक्टर जी) => (ए -> बी) -> एफ (जी ए) -> एफ (जी बी) 'के सही प्रकार के हस्ताक्षर देने की आवश्यकता होगी। –

+6

@AaditMShah उस मामले में, मैं '<$$> = fmap जैसे कुछ पसंद करूंगा। fmap', चूंकि '(। :) '' (->) r' के लिए विशिष्ट सम्मेलन द्वारा है, और" बाहरी "' fmap' वैसे भी '(->) आर' मज़ेदार पर है। – kqr

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