2016-11-10 11 views
7

क्या साधारण फ़ंक्शन संरचना के लिए "नोटेशन" वाक्य रचनात्मक चीनी है?फंक्शन संरचना नोटेशन

(यानी (.) :: (b -> c) -> (a -> b) -> a -> c)

मैं बाद में करने के लिए कुछ रचनाओं के परिणाम स्टोर करने के लिए सक्षम होने के लिए चाहते हैं (जबकि अभी भी श्रृंखला जारी है।

मैं नहीं बल्कि यदि संभव हो तो RebindableSyntax एक्सटेंशन का उपयोग नहीं चाहते हैं।

मैं कुछ इस तरह की तलाश में हूँ:

composed :: [String] -> [String] 
composed = do 
    fmap (++ "!!!") 
    maxLength <- maximum . fmap length 
    filter ((== maxLength) . length) 

composed ["alice", "bob", "david"] 
-- outputs: ["alice!!!", "david!!!"] 

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

शायद मैं ऐसा कुछ राज्य मोनड के साथ कर सकता हूं?

आपकी मदद के लिए धन्यवाद!

संपादित

बात इस तरह की थोड़े काम करता है: जैसे कि तीर हैं कुछ प्राप्त करने के लिए

split :: (a -> b) -> (b -> a -> c) -> a -> c 
split ab bac a = bac (ab a) a 

composed :: [String] -> [String] 
composed = do 
    fmap (++ "!!!") 
    split 
     (maximum . fmap length) 
     (\maxLength -> (filter ((== maxLength) . length))) 
+0

कृपया क्या रचना आप वास्तव में यहाँ प्राप्त करने के लिए कोशिश कर रहे हैं निर्दिष्ट करें। कम से कम, उस प्रकार के हस्ताक्षर दें जिसे आप 'रचित' के लिए चाहते हैं! – leftaroundabout

+0

बस सामान्य फ़ंक्शन संरचना, लेकिन बाद के फ़ंक्शन कॉल में उपयोग के लिए अंतरालीय परिणामों को स्टोर करने की क्षमता के साथ (उदाहरण देखें)। मैंने टाइप जोड़ा। –

+1

क्या कोई कारण है कि आपको इसे पॉइंट-फ्री शैली में लिखना होगा? यह गैर-बिंदु-मुक्त शैली में आसान होना चाहिए। – immibis

उत्तर

2

leftaroundabout के रूप में उल्लेख किया है, आप Arrows उपयोग कर सकते हैं अपने समारोह लिखने के लिए। लेकिन, ghc हास्केल कंपाइलर में एक सुविधा है, जो proc-तीरों के लिए नोटेशन है। यह अच्छी तरह से ज्ञात do-नोटेशन के समान है, लेकिन दुर्भाग्यवश, बहुत से लोगों को इसके बारे में पता नहीं है।

proc साथ

-notation आप अगले अधिक redable और सुरुचिपूर्ण तरीके से अपने वांछित समारोह लिख सकते हैं:

{-# LANGUAGE Arrows #-} 

import Control.Arrow (returnA) 
import Data.List  (maximum) 

composed :: [String] -> [String] 
composed = proc l -> do 
    bangedL <- fmap (++"!!!")  -< l 
    maxLen <- maximum . fmap length -< bangedL 
    returnA -< filter ((== maxLen) . length) bangedL 

और इस रूप में GHCi में काम करता है उम्मीद:

ghci> composed ["alice", "bob", "david"] 
["alice!!!","david!!!"] 

आप रुचि रखते हैं , आप समझने के लिए अच्छे चित्रों के साथ कुछ ट्यूटोरियल पढ़ सकते हैं कि तीर क्या है और यह शक्तिशाली सुविधा कैसे काम करती है ताकि आप इसमें गहराई से गोता लगा सकें:

https://www.haskell.org/arrows/index.html

https://en.wikibooks.org/wiki/Haskell/Understanding_arrows

+1

उल्लेख करने के लिए अच्छा है, हालांकि मैं कहूंगा कि 'proc' नोटेशन की ताकत है कि यह आपको काफी सामान्य तीर रचनाओं को लिखने की अनुमति देता है _as if_ वे लैम्ब्डा वाक्यविन्यास में कार्यरत थे। यदि श्रेणी आप _is_ '(->) 'पर काम कर रहे हैं, तो एक ही चीज़ को पूरा करने के लिए बाध्यकारी में' lambdas/'let ... in' का उपयोग न करने का अधिक कारण नहीं है, यदि आप उन मध्यवर्ती को देने जा रहे हैं वैसे भी परिणाम नाम। – leftaroundabout

+0

@ बाएंअराउंडबाउट हां, '(->) 'के अलावा तीर मौजूद हैं और आप सामान्य संस्करण फ़ंक्शन रचनाएं लिखना चाहेंगे हालांकि मैंने इस मामले के लिए उदाहरण को आसान बनाने के लिए ऐसा नहीं किया है। आपका संस्करण इस ब्लॉग-पोस्ट से विचारों का पालन करता है: http://degoes.net/articles/insufficiently-polymorphic लेकिन कभी-कभी परिवर्तनीय नाम होने से कोड समझदारी (विशेष रूप से तीरों के लिए) आसानी हो सकती है। – Shersh

5

एक संभव तरीका है। असल में, "अंतरालीय परिणामों को संग्रहित करने" में आप केवल रचना श्रृंखला के माध्यम से सूचना प्रवाह को विभाजित कर रहे हैं। यही है &&& (fanout) संयोजक करता है।

import Control.Arrow 

composed = fmap (++ "!!!") 
     >>> ((. length) . (==) . maximum . fmap length &&& id) 
     >>> uncurry filter 

हालांकि यह निश्चित रूप से अच्छा मानव-समझदार कोड नहीं है।

एक राज्य मोनड कुछ संबंधित भी अनुमति देता प्रतीत होता है, लेकिन समस्या यह है कि राज्य का प्रकार do ब्लॉक की मोनैडिक श्रृंखला के माध्यम से तय किया जाता है। यह संरचना श्रृंखला में अलग-अलग टाइप किए गए मानों को चुनने के लिए पर्याप्त रूप से लचीला नहीं है। हालांकि यह निश्चित रूप से इसे रोकने के लिए संभव है (उनमें से, वास्तव में, RebindableSyntax), यह भी आईएमओ का एक अच्छा विचार नहीं है।

+0

हरम, दिलचस्प! मैंने ऊपर दिए गए संपादन में कुछ भी कोशिश की है, फिर भी मुझे कुछ और अधिक सुरुचिपूर्ण देखना अच्छा लगेगा, हालांकि यह थोड़ा अजीब है:/ –

1

आपके पास अनिवार्य रूप से एक फ़िल्टर है, लेकिन एक जहां फ़िल्टरिंग फ़ंक्शन बदलता है जब आप सूची में पुन: प्रयास करते हैं। मैं एक "काँटेदार" संरचना के रूप में यह नहीं मॉडल होगा, लेकिन निम्नलिखित समारोह f :: String -> (Int, [String]) का उपयोग कर एक गुना के रूप में:

  1. वापसी मान वर्तमान अधिकतम और उस अवधि के सभी तार बनाए रखता है।
  2. यदि पहला तर्क वर्तमान अधिकतम से छोटा है, तो इसे छोड़ दें।
  3. यदि पहला तर्क वर्तमान अधिकतम के समान है, तो इसे सूची में जोड़ें।
  4. यदि पहला तर्क लंबा है, तो इसकी लंबाई नई अधिकतम बनाएं, और वर्तमान आउटपुट सूची को एक नई सूची के साथ बदलें।

एक बार गुना पूरा हो जाने के बाद, आप बस टुपल से सूची निकालें।

-- Not really a suitable name anymore, but... 
composed :: [String] -> [String] 
composed = snd . foldr f (0, []) 
    where f curr (maxLen, result) = let currLen = length curr 
            in case compare currLen maxLen of 
             LT -> (maxLen, result)  -- drop 
             EQ -> (maxLen, curr:result) -- keep 
             GT -> (length curr, [curr]) -- reset 
3

(<*>) के प्रकार Applicative के समारोह उदाहरण के लिए विशेष है:

(<*>) :: (r -> a -> b) -> (r -> a) -> (r -> b) 

जिसके परिणामस्वरूप r -> b समारोह अपने तर्क गुजरता है करने के लिए दोनों r -> a -> b और r -> a काम करता है, और उसके बाद का उत्पादन किया a मान का उपयोग करता r -> a द्वारा r -> a -> b एक के दूसरे तर्क के रूप में कार्य करें।

यह आपके कार्य के साथ क्या करना है? filter दो तर्कों, एक अनुमान और एक सूची का एक कार्य है। अब, आप जो करने का प्रयास कर रहे हैं उसका एक प्रमुख पहलू यह है कि सूची से पूर्वानुमान उत्पन्न होता है। इसका मतलब है कि आपके समारोह के मुख्य (<*>) के रूप में व्यक्त किया जा सकता है:

-- Using the predicate-generating function from leftaroundabout's answer. 
maxLengthOnly :: Foldable t => [t a] -> [t a] 
maxLengthOnly = flip filter <*> ((. length) . (==) . maximum . fmap length) 

composed :: [String] -> [String] 
composed = maxLengthOnly . fmap (++ "!!!") 

यह maxLengthOnly परिभाषा एक काफी अच्छा एक लाइनर हो सकता है अगर pointfree विधेय पैदा समारोह इतना भद्दा नहीं थे।

कार्यों का Applicative उदाहरण के बाद से Monad एक करने के लिए सत्ता में बराबर है, maxLengthOnly भी phrased जा सकता है के रूप में:

maxLengthOnly = (. length) . (==) . maximum . fmap length >>= filter 

(split आप अपने प्रश्न को जोड़ा गया, वैसे, (>>=) कार्यों के लिए है ।)

Applicative साथ इसे लिखने का एक अलग तरीका है:

maxLengthOnly = filter <$> ((. length) . (==) . maximum . fmap length) <*> id 

यह कोई संयोग नहीं है कि यह बाएंअराउंडबाउट के समाधान की तरह दिखता है: कार्यों के लिए, (,) <$> f <*> g = liftA2 (,) f g = f &&& g

अंत में, यह भी ध्यान देने योग्य है कि, यह fmap (++ "!!!") साथ maxLengthOnly के नवीनतम संस्करण में id को बदलने के लिए, ऐसा इसलिए है क्योंकि fmap (++ "!!!") तार की लंबाई बदल जाता है, और इसलिए काम नहीं करेगा आकर्षक है, जबकि के परिणाम को प्रभावित करता है के लायक है विधेय। एक समारोह है कि विधेय को अमान्य नहीं है के साथ है, हालांकि, यह बहुत अच्छी तरह से काम करेगा:

nicerComposed = filter 
    <$> ((. length) . (==) . maximum . fmap length) <*> fmap reverse 
GHCi> nicerComposed ["alice","bob","david"] 
["ecila","divad"] 
संबंधित मुद्दे