2011-12-30 41 views
8

मैं इस समय 20 Intermediate Haskell Exercises के माध्यम से काम कर रहा हूं, जो कि एक मजेदार व्यायाम है। इसमें टाइपक्लास Functor और Monad (और Functor एस और Monad एस तर्क के रूप में कार्य करने वाले फ़ंक्शन) के साथ-साथ Furry और Misty जैसे प्यारे नामों के साथ हम जो भी कर रहे हैं उसे छिपाने के लिए (कुछ रोचक कोड के लिए बनाता है) के विभिन्न उदाहरणों को लागू करना शामिल है।पॉइंटफ्री शैली में फ़ंक्शन लिखने के लिए सामान्य योजना क्या है?

मैं इसे कुछ बिंदु-मुक्त शैली में करने की कोशिश कर रहा हूं, और मुझे आश्चर्य हुआ कि क्या पॉइंट-फुल (?) परिभाषा को बिंदु-मुक्त परिभाषा में बदलने के लिए एक सामान्य योजना है। उदाहरण के लिए, यहाँ Misty के लिए typeclass है:

class Misty m where 
    unicorn :: a -> m a 
    banana :: (a -> m b) -> m a -> m b 

(कार्यों unicorn और bananareturn और >>= कर रहे हैं, के मामले में यह स्पष्ट नहीं है) और यहाँ apple की मेरी कार्यान्वयन है (flip ap के बराबर):

apple :: (Misty m) => m a -> m (a -> b) -> m b 
apple x f = banana (\g -> banana (unicorn . g) x) f 

अभ्यास के बाद भागों आप को लागू है संस्करणों की liftM, liftM2 आदि यहाँ मेरी समाधान हैं:

appleTurnover :: (Misty m) => m (a -> b) -> m a -> m b 
appleTurnover = flip apple 

banana1 :: (Misty m) => (a -> b) -> m a -> m b 
banana1 = appleTurnover . unicorn 

banana2 :: (Misty m) => (a -> b -> c) -> m a -> m b -> m c 
banana2 f = appleTurnover . banana1 f 

banana3 :: (Misty m) => (a -> b -> c -> d) -> m a -> m b -> m c -> m d 
banana3 f x = appleTurnover . banana2 f x 

banana4 :: (Misty m) => (a -> b -> c -> d -> e) -> m a -> m b -> m c -> m d -> m e 
banana4 f x y = appleTurnover . banana3 f x y 

अब, banana1 (समकक्ष liftM करने के लिए या fmap) मैं pointfree शैली में लागू करने के लिए, appleTurnover का एक उपयुक्त परिभाषा से कर रहा था। लेकिन अन्य तीन कार्यों के साथ मुझे पैरामीटर का उपयोग करना पड़ा।

मेरा प्रश्न है: इन तरह की परिभाषाओं को पॉइंट-फ्री परिभाषाओं में बदलने के लिए एक नुस्खा है?

+0

यह अमूर्त उन्मूलन तुम क्या से कनेक्ट हो गया लैम्ब्डा कैलकुस एक्सप्रेशन को संयोजकों में बदलने के लिए। आप स्टैंडअलोन [पॉइंटफ्री] (http://hackage.haskell.org/package/pointfree) टूल भी देख सकते हैं ([lambdabot] में '@ pl' के रूप में भी उपलब्ध है) (http://www.haskell.org/haskellwiki/Lambdabot))। – ehird

+0

[एक संबंधित चर्चा मैं दूसरे दिन किसी मित्र के साथ थी] (https://gist.github.com/1507246)। आपको यह दिलचस्प लगेगा। – missingfaktor

उत्तर

11

जैसा कि pointfree उपयोगिता द्वारा दिखाया गया है, यह स्वचालित रूप से ऐसा कोई रूपांतरण करना संभव है। हालांकि, परिणाम अधिक बार सुधार से obfuscated है। यदि किसी का लक्ष्य इसे नष्ट करने की बजाय सुगमता को बढ़ाने के लिए है, तो पहला लक्ष्य की पहचान करना चाहिए क्यों एक अभिव्यक्ति में एक विशेष संरचना है, एक उपयुक्त अमूर्तता ढूंढें, और इस तरह चीजों को बनाएं।

सबसे सरल संरचना एक रैखिक पाइपलाइन में चीजों को एक साथ जोड़ रही है, जो सादा कार्य संरचना है। यह हमें अपने आप पर बहुत दूर ले जाता है, लेकिन जैसा कि आपने देखा है कि यह सब कुछ संभाल नहीं करता है।

एक सामान्यीकरण अतिरिक्त तर्कों के साथ कार्य करना है, जिसे वृद्धिशील रूप से बनाया जा सकता है। यहां एक उदाहरण दिया गया है: onResult = (. (.)) परिभाषित करें।अब, onResult एन बार id के शुरुआती मूल्य पर लागू करने से आपको एन-आर्य फ़ंक्शन के परिणामस्वरूप फ़ंक्शन संरचना मिलती है। इसलिए हम comp2 = onResult (.) को परिभाषित कर सकते हैं, और फिर NAND ऑपरेशन को परिभाषित करने के लिए comp2 not (&&) लिखें।

एक और सामान्यीकरण - जो ऊपर, वास्तव में शामिल हैं - ऑपरेटरों कि एक बड़ा मूल्य के एक घटक के लिए एक समारोह लागू परिभाषित करने के लिए है। यहां एक उदाहरण first और secondControl.Arrow में होगा, जो 2-टुपल्स पर काम करता है। Conal Elliott's Semantic Editor Combinators इस दृष्टिकोण पर आधारित हैं।

एक थोड़ा अलग मामला है जब आप कुछ प्रकार b पर एक बहु तर्क कार्य हो रहा है, और एक समारोह a -> b, और उन्हें a का उपयोग कर एक बहु तर्क समारोह में गठबंधन करने के लिए की जरूरत है। 2-ary कार्यों की सामान्य स्थिति के लिए, मॉड्यूल Data.Functionon Combinator, जो आप compare `on` fst की तरह भाव लिखने के अपने पहले तत्वों पर 2-tuples तुलना करने के लिए उपयोग कर सकते हैं प्रदान करता है।

यह एक जटिल काम मुद्दा है जब एक ही तर्क एक बार से अधिक प्रयोग किया जाता है, लेकिन वहाँ सार्थक आवर्ती यहाँ पैटर्न है कि यह भी निकाला जा सकता है कर रहे हैं। यहां एक आम मामला एक ही तर्क के लिए कई कार्यों को लागू कर रहा है, फिर परिणामों को किसी अन्य फ़ंक्शन के साथ एकत्रित कर रहा है। यह कार्यों के लिए Applicative उदाहरण के अनुरूप होता है, जो हमें (&&) <$> (> 3) <*> (< 9) जैसे अभिव्यक्ति लिखने देता है ताकि यह जांच सके कि कोई संख्या किसी दिए गए सीमा में पड़ती है या नहीं।

महत्वपूर्ण बात यह है कि, यदि आप वास्तविक कोड में इनमें से किसी का भी उपयोग करना चाहते हैं, तो यह सोचने के लिए कि का अर्थ और संरचना में कैसा दिखाई देता है, इस बारे में सोचना है। आप ऐसा कर, और फिर सार्थक combinators का उपयोग कर pointfree शैली में यह refactor हैं, तो आप अक्सर कोड स्पष्ट की तुलना में यह अन्यथा होगा की मंशा, pointfree की विशिष्ट उत्पादन के विपरीत बना देंगे।

+0

alsome answer = o – Claudiu

5

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

banana2 f = appleTurnover . banana1 f 
      = (.) appleTurnover (banana1 f) 
      = ((.) appleTurnOver) . banana1 $ f 
banana2 = (appleTurnover .) . banana1 

पॉइंटफ्री उपयोगिता के लिए स्रोत कोड में अधिक शामिल है, लेकिन यह बहुत से मामलों को संभालता है।

banana4 f x y = appleTurnover . banana3 f x y 
       = (.) appleTurnover ((banana3 f x) y) 
       = ((.) appleTurnover) . (banana3 f x) $ y 
banana4 f x = ((.) appleTurnover) . (banana3 f x) 
      = (.) ((.) appleTurnover) (banana3 f x) 
      = ((.) ((.) appleTurnover)) ((banana3 f) x) 
      = ((.) ((.) appleTurnover)) . (banana3 f) $ x 
banana4 f = ((.) ((.) appleTurnover)) . (banana3 f) 
      = (.) ((.) ((.) appleTurnover)) (banana3 f) 
      = ((.) ((.) ((.) appleTurnover))) (banana3 f) 
      = ((.) ((.) ((.) appleTurnover))) . banana3 $ f 
banana4 = ((.) ((.) ((.) appleTurnover))) . banana3 
     = (((appleTurnover .) .) .) . banana3 
+6

यह भी आपके कार्यों को पूरी तरह से अपठनीय बनाने का एक अच्छा तरीका है ... –

+4

'वापसी' को 'यूनिकॉर्न' कहा जाता है ऐसा लगता है कि ओपी उस = पी के बारे में चिंतित नहीं है। – Claudiu

+0

@ क्लाउडियो: ठीक है, यह ओपी कर रहे अभ्यासों से आ रहा है, जो मूल रूप से परिभाषाओं के लिए बहुत मूर्ख नामों का उपयोग करके जमीन से 'मोनाद' जैसी चीजें प्राप्त कर रहे हैं। –

2

एक pointfree पैकेज है जो एक Haskell समारोह परिभाषा लेता है और एक pointfree शैली में इसे फिर से लिखने के लिए प्रयास करता है। मैं नए विचार पाने के लिए इसके साथ प्रयोग करने का सुझाव दूंगा। अधिक जानकारी के लिए this page देखें; पैकेज here उपलब्ध है।

3

मैं निम्नलिखित अवधि में पुनः लेखन प्रणाली का उपयोग करें:

\x -> f x ------> f 
f y x ----------> flip f x y 
\x -> f (g x) --> f . g 

यह अधूरा है (combinatory तर्क के बारे में किताबों में क्यों पढ़ें), लेकिन यह पर्याप्त है:

banana2 f = appleTurnover . banana1 f 
:

यहाँ banana2 है

banana2 = \f -> appleTurnover . banana1 f 
:

एक लैम्ब्डा के रूप में फिर से लिखें

लिखें उपसर्ग शैली में (।):

banana2 = \f -> (.) appleTurnover (banana1 f) 

ध्यान दें कि

banana2 = \f -> ((.) appleTurnover) (banana1 f) 

तो नियम 3 लागू किया जा सकता।sequence

B f g x = f (g x)  -- (.) , <$> for ((->) a) 
C f x y = f y x  -- flip 
K x y = x    -- const 
I x = x    -- id 
S f g x = f x (g x) -- <*> , ap for ((->) a) 
W f x = f x x   -- join 
(f >>= g) x = g (f x) x 
(f =<< g) x = f (g x) x 

बार liftMx, liftAx में: f(.) appleTurnover है और gbanana है: चूंकि pointfree शैली combinators शैली है, बस में जाना जाता combinators परिभाषाएँ लागू

banana2 = ((.) appleTurnover) . banana1 
0

, उन्हें पीछे की ओर पढ़ने प्रतिस्थापन बनाने के लिए , sequenceA चीजों को सरल बना सकता है। मैं foldr, unfoldr, iterate, until आदि को मूल संयोजकों के रूप में भी मानता हूं।

अक्सर, ऑपरेटर वर्गों का उपयोग कर भी मदद करता है:

op a b = (a `op` b) = (`op` b) a = (a `op`) b 

कुछ पैटर्न परिचित और हां, तो सीधे इस्तेमाल किया हो सकता है:

((f .) . g) x y = f (g x y) 
((. f) . g) x y = g x (f y) 

(((f .) .) . g) x y z = (f .) (g x y) z = f (g x y z) 
(((. f) .) . g) x y z = (. f) (g x y) z = g x y (f z) 

आदि