2014-06-17 17 views
17

के बीच अंतर मैंने हास्केल सीखना शुरू किया और मुझे एक समस्या का सामना करना पड़ा जिसे मैं समझ नहीं सकता। मैं एक विधि (this page से) कुंजी-मान सूची की एक सूची से मूल्य को खोजने के लिए इस्तेमाल किया मिल गया है:

let findKey key xs = snd . head . filter (\(k,v) -> key == k) $ xs 

मैं थोड़ा के साथ नगण्य की कोशिश की और इस तरह से $ साइन से छुटकारा पाने का फैसला किया:

let findKey key xs = snd . head . filter (\(k,v) -> key == k) (xs) 

हालांकि, यह भी विश्लेषण नहीं करता है (फिल्टर बहुत अधिक तर्क त्रुटि पर लागू होता है)। मैंने पढ़ा है कि $ संकेत का उपयोग केवल ब्रांड्स को प्रतिस्थापित करने के लिए किया जाता है और मुझे पता नहीं लगा सकता कि कोड का यह सरल परिवर्तन खराब क्यों है। क्या कोई इसे मुझे समझा सकता है?

+3

[Haskell: के बीच अंतर) के संभावित डुप्लिकेट। (डॉट) और $ (डॉलर चिह्न)] (http://stackoverflow.com/questions/940382/haskell-difference-between-dot-and-dollar-sign) – cdk

उत्तर

29

है इन्फ़िक्स ऑपरेटर ($) बस है " समारोह आवेदन "। दूसरे शब्दों में

f x  -- and 
f $ x 

समान हैं। चूंकि हास्केल में कोष्ठकों केवल पूर्वता को स्पष्ट करने के लिए उपयोग किया जाता है (और टपल संकेतन और a few other minor places, see comments के लिए) हम भी ऊपर कुछ अन्य तरीके

f  x 
f $ x 
(f) x 
f (x) 
(f) (x) -- and even 
(f) $ (x) 

हर मामले में, इसके बाद के संस्करण भाव एक ही बात को निरूपित में लिख सकते हैं: "लागू तर्क f तर्क x पर "।

तो यह सब वाक्यविन्यास क्यों है? ($) दो कारणों

  1. के लिए उपयोगी है यह है वास्तव में कम पूर्वता तो यह कभी कभी कोष्ठकों का एक बहुत के लिए में खड़े हो सकते हैं
  2. यह समारोह आवेदन की कार्रवाई के लिए एक स्पष्ट नाम होना अच्छा रहेगा जो

पहले मामले में, निम्नलिखित गहरा राइट नेस्टेड समारोह आवेदन

f (g (h (i (j x)))) 
पर विचार

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

f $ g $ h $ i $ j $ x 

कुछ लोगों को यह पढ़ने में आसान लगता है।अधिक आधुनिक शैली भी क्रम में (.) को शामिल किया गया पर जोर देना है कि इस वाक्यांश के पूरे बाईं ओर बस कार्यों

f . g . h . i . j $ x 

की एक रचना पाइप लाइन है और यह वाक्यांश, जैसा कि हम ऊपर देखा

(f . g . h . i . j) x 
के लिए है, समान

जो कभी-कभी पढ़ने के लिए अच्छा होता है।


ऐसे समय भी होते हैं जब हम फ़ंक्शन एप्लिकेशन के विचार को पार करने में सक्षम होना चाहते हैं। उदाहरण के लिए, अगर हम कार्यों की एक सूची है

lof :: [Int -> Int] 
lof = [ (+1), (subtract 1), (*2) ] 

हम एक समारोह

> map (\fun -> fun 4) lof 
[ 5, 3, 8 ] 

को नंबर 4 लागू होते हैं, उन पर एक मूल्य के द्वारा आवेदन मैप करने के लिए उदाहरण के लिए चाहते हो सकता है लेकिन चूंकि यह सिर्फ है समारोह आवेदन, हम भी ($) से अधिक खंड सिंटैक्स का उपयोग कर सकते हैं थोड़ा और स्पष्ट

> map ($ 4) lof 
[ 5, 3, 8 ] 
+0

और कैसे आप कोष्ठक के साथ डॉट की जगह है? यह '(च। जी। एच। मैं (जे)) x' काम करने के लिए प्रतीत नहीं होता ... – Niemand

+0

' (च। जी। ज) x' = 'च $ जी $ hx' – user2407038

+0

मैं समझता हूँ कि आप नहीं कर सकते इस तरह '' '' के साथ स्वतंत्र रूप से '.' को प्रतिस्थापित करें। लेकिन क्या आप इसे '()' के साथ भी कर सकते हैं? – Niemand

15

ऑपरेटर $, सबसे कम प्राथमिकता है, इसलिए

snd . head . filter (\(k,v) -> key == k) $ xs 

(snd . head . filter (\(k,v) -> key == k)) xs 

के रूप में पढ़ा है, जबकि अपने दूसरे अभिव्यक्ति बल्कि

snd . head . (filter (\(k,v) -> key == k) xs) 
4

"$ हस्ताक्षर करने के लिए प्रयोग किया जाता है होना करने के लिए बस कोष्ठक को प्रतिस्थापित करें "काफी सही है - हालांकि, यह दोनों पक्षों को प्रभावी रूप से सबकुछ संश्लेषित करता है! तो

snd . head . filter (\(k,v) -> key == k) $ xs 

प्रभावी रूप से

(snd . head . filter (\(k,v) -> key == k)) (xs) 
बेशक

है, कोष्ठक आसपास xs यहाँ (है कि एक "परमाणु" वैसे भी) अनावश्यक हैं, इसलिए प्रासंगिक लोगों बाईं ओर चारों ओर इस मामले में कर रहे हैं। वास्तव में यह अक्सर हास्केल में होता है, क्योंकि सामान्य दर्शन कुछ तर्कों के लिए फ़ंक्शन को लागू करते समय शामिल विशेष मूल्यों के बजाय, यथासंभव सार तत्वों के रूप में कार्यों के बारे में सोचना है। आपकी परिभाषा भी

let findKey key xs' = let filter (\(k,v) -> key == k) $ xs 
          x0 = head xs' 
          v0 = snd x0 
         in v0 

यह बेहद स्पष्ट होगा, लेकिन उन सभी मध्यवर्ती मूल्य वास्तव में दिलचस्प नहीं हैं। तो हम . के साथ, "पॉइंट-फ्री" कार्यों को एक साथ जोड़ना पसंद करते हैं। यही कारण है कि अक्सर हमें अपनी परिभाषा के लिए वास्तव में बॉयलरप्लेट का एक बहुत से छुटकारा मिलता है, निम्नलिखित किया जा सकता है:

  1. xs की η-कमी। यही कारण है कि तर्क सिर्फ कार्यों की श्रृंखला को पारित, तो हम साथ ही कहते हैं, "findKey key" है कि चेन, जो कुछ भी तर्क आपूर्ति "के साथ

    findKey key = snd . head . filter (\(k,v) -> key == k) 
    
  2. इसके बाद, हम इस स्पष्ट लैम्ब्डा से बच सकते हैं: \(k,v) -> k बस fst समारोह, के बाद से बहुत ज्यादा बिंदु मुक्त व्यर्थ और पढ़ने योग्य नहीं है है। इसके बाद आप key के साथ तुलना postcompose की जरूरत है।

    findKey key = snd . head . filter ((key==) . fst) 
    
  3. मैं यहाँ रोक चाहते हैं। लेकिन आप पर जा सकते हैं: key पर तर्क के आसपास अब नए माता-पिता हैं, हम फिर से $ वाले लोगों से छुटकारा पा सकते हैं। लेकिन सावधान:

    "findKey key = snd . head . filter $ (key==) . fst" 
    

    नहीं सही है, क्योंकि $ फिर दोनों पक्षों parenthesise हैं, फिर भी (snd . head . filter) नहीं अच्छी तरह से लिखा गया। असल में snd . head केवल filter दोनों तर्कों के बाद आना चाहिए। इस तरह के एक के बाद के बाद रचना करने के लिए एक संभव तरीका समारोह functor उपयोग कर रहा है:

    findKey key = fmap (snd . head) . filter $ (key==) . fst 
    

    ... हम पर जाने के भी आगे और key चर का भी छुटकारा मिल सकता है, लेकिन यह नहीं होगा अच्छे लग रहे हो। मुझे लगता है कि आप बिंदु मिल गया है ...

7

$ संकेत कोष्ठकों की जगह के लिए जादू सिंटैक्स नहीं है। यह एक साधारण इंफिक्स ऑपरेटर है, हर तरह से ऑपरेटर + है।

(xs) की तरह एक ही नाम के आसपास कोष्ठक लाना हमेशा बस xs करने के लिए बराबर है। तो अगर $ किया गया है, तो आपको वही त्रुटि मिल जाएगी।

let findKey key xs = snd . head . filter (\(k,v) -> key == k) + xs 

तथ्य यह है कि नंबर पर + काम करता है तो यह कोई मतलब नहीं है पर ध्यान न दें, और बस: कल्पना करना क्या हुआ अगर आप इस तरह के + रूप में कुछ अन्य ऑपरेटर तुम वहाँ से परिचित हैं, था क्या होगा

कोशिश अभिव्यक्ति की संरचना के बारे में सोचें; कौन से नियमों को कार्यों के रूप में पहचाना जा रहा है, और तर्क के रूप में उन्हें कौन से नियम पारित किए जा रहे हैं।

वास्तव में, + का उपयोग करके वास्तव में पार्स और टाइपशेक सफलतापूर्वक उपयोग करता है! (यह आपको बकवास प्रकार वर्ग की बाधाओं के साथ एक समारोह देता है, लेकिन यदि आप उन्हें पूरा करते हैं तो इसका मतलब कुछ है)। चलो के माध्यम से चलना कैसे इन्फ़िक्स ऑपरेटरों हल कर रहे हैं:

let findKey key xs = snd . head . filter (\(k,v) -> key == k) + xs 

सर्वोच्च प्राथमिकता बात हमेशा सामान्य कार्य अनुप्रयोग है (बस एक दूसरे के बगल शब्द लिखना, कोई इन्फ़िक्स शामिल ऑपरेटरों के साथ)। यहां केवल एक उदाहरण है, filter लैम्ब्डा परिभाषा पर लागू होता है। यही कारण है कि "का समाधान" हो जाता है, और जहाँ तक पार्स करने ऑपरेटरों के बाकी का संबंध है के रूप में एक उप अभिव्यक्ति हो जाता है:

let findKey key xs 
     = let filterExp = filter (\(k,v) -> key == k) 
     in snd . head . fileterExp + xs 

अगले सर्वोच्च प्राथमिकता बात . ऑपरेटर है। हमारे पास यहां से चुनने के लिए कई मिल गए हैं, सभी एक ही प्राथमिकता के साथ। . सही सहयोगी है, इसलिए हम सबसे पहले सबसे पहले लेते हैं (लेकिन यह वास्तव में परिणाम को नहीं बदलेगा जिसे हम चुनते हैं, क्योंकि का एक सहयोगी ऑपरेशन है, लेकिन पार्सर को यह जानने का कोई तरीका नहीं है):

let findKey key xs 
     = let filterExp = filter (\(k,v) -> key == k) 
      dotExp1 = head . filterExp 
     in snd . dotExp1 + xs 

ध्यान दें कि . अपने बाएँ और दाएँ करने के लिए तुरंत मामले पकड़ा। यही कारण है कि प्राथमिकता बहुत महत्वपूर्ण है।वहाँ अभी भी एक . बाईं, जो + से अभी भी उच्च पूर्वता है, जिससे वह अगले जाता है:

let findKey key xs 
     = let filterExp = filter (\(k,v) -> key == k) 
      dotExp1 = head . filterExp 
      dotExp2 = snd . dotExp1 
     in dotExp2 + xs 

और हम काम हो गया! + में ऑपरेटरों की सबसे कम प्राथमिकता है, इसलिए यह अपने तर्कों को अंतिम रूप देता है, और पूरी अभिव्यक्ति में सबसे ज्यादा कॉल होने के बाद समाप्त होता है। ध्यान दें कि + कम प्राथमिकता होने से xs को "दावा" किया जा रहा है, जो किसी भी उच्च प्राथमिकता अनुप्रयोगों के बाईं ओर तर्क के रूप में है। और यदि उनमें से कोई भी कम प्राथमिकता थी, तो वे पूरी अभिव्यक्ति dotExp2 + xs को तर्क के रूप में ले लेते थे, इसलिए उन्हें अभी भी xs नहीं मिल सका; xs (किसी भी इंफिक्स ऑपरेटर से पहले एक इंफिक्स ऑपरेटर डालने से) इसे बाएं से कुछ भी तर्क के रूप में दावा करने से रोकता है।

इस तथ्य में है वास्तव में उसी तरह है कि $, इस अभिव्यक्ति में पार्स किया गया है क्योंकि . और $ एक ही रिश्तेदार पूर्वता . और + है कि राशि के लिए हो; $ को बहुत कम प्राथमिकता के लिए डिज़ाइन किया गया है, इसलिए यह बाएं और दाएं से जुड़े लगभग किसी भी अन्य ऑपरेटरों के साथ इस तरह काम करेगा।

let findKey key xs = snd . head . filter (\(k,v) -> key == k) xs 

सामान्य समारोह आवेदन पहले से चला जाता है:

अगर हम नहींfilter कॉल और xs के बीच एक इन्फ़िक्स ऑपरेटर रखूँ , तो यह क्या होता है। यहां हमारे पास एक-दूसरे के बगल में 3 शब्द हैं: filter, (\(k,v) -> key == k), और xs। समारोह आवेदन बाईं साहचर्य है, इसलिए हम पहले वाम-पंथी जोड़ी ले:

let findKey key xs 
     = let filterExp1 = filter (\(k,v) -> key == k) 
     in snd . head . filterExp1 xs 

अभी भी एक और सामान्य आवेदन बाईं, जो अभी भी . की तुलना में अधिक पूर्वता है है, तो हम ऐसा:

let findKey key xs 
     = let filterExp1 = filter (\(k,v) -> key == k) 
      filterExp2 = filterExp1 xs 
     in snd . head . filterExp2 

अब पहले डॉट:

let findKey key xs 
     = let filterExp1 = filter (\(k,v) -> key == k) 
      filterExp2 = filterExp1 xs 
      dotExp = head . filterExp2 
     in snd . dotExp 

और हम काम हो गया, पूरी अभिव्यक्ति में सर्वोच्च कॉल इस समय सबसे बाईं ओर . ऑपरेटर था। इस बार xs को filter पर दूसरे तर्क के रूप में चूसा गया; यह एक प्रकार है जहां हम चाहते हैं कि filter दो तर्क लेता है, लेकिन यह filter फ़ंक्शन संरचना श्रृंखला में छोड़ देता है, और filter दो तर्कों पर लागू होता है, कोई फ़ंक्शन वापस नहीं कर सकता है। हम जो चाहते थे उसे एक पर फ़ंक्शन देने के लिए तर्क लागू करना था, यह फ़ंक्शन फ़ंक्शन संरचना श्रृंखला का हिस्सा हो, और उसके बाद संपूर्ण फ़ंक्शन xs पर लागू करें।

वहाँ $ के साथ, अंतिम रूप दर्पण है कि जब हम + प्रयोग किया है:

let findKey key xs 
     = let filterExp = filter (\(k,v) -> key == k) 
      dotExp1 = head . filterExp 
      dotExp2 = snd . dotExp1 
     in dotExp2 $ xs 

यह वास्तव में जब हम + था के रूप में एक ही तरह से पार्स है, इसलिए फर्क सिर्फ इतना है कि जहां + का अर्थ है "अपने बाएं जोड़ने है मेरे दाहिने तर्क के लिए तर्क ", $ का अर्थ है" मेरे दाएं तर्क को मेरे दाएं तर्क में एक फ़ंक्शन के रूप में लागू करें "।हम यही करना चाहते थे! हुज़्ज़ाह!

TLDR: बुरी खबर है कि $ सिर्फ कोष्ठकों लपेटकर द्वारा काम नहीं करता है, यह उससे अधिक जटिल है। अच्छी खबर यह है कि यदि आप समझते हैं कि हास्केल इन्फ़िक्स ऑपरेटरों को शामिल भाव का समाधान करता है, तो आप कैसे $ काम करता है समझते हैं। जहां तक ​​भाषा का संबंध है, इसके बारे में बिल्कुल कुछ भी नहीं है; यह एक साधारण ऑपरेटर है जिसे आप स्वयं परिभाषित कर सकते हैं यदि यह अस्तित्व में नहीं था।


(+) जैसे किसी ऑपरेटर Parenthesising भी केवल आपके ठीक उसी समारोह + से दर्शाया जाता है देता है, लेकिन अब यह विशेष इन्फ़िक्स वाक्य रचना नहीं है, तो यह प्रभावित करता है कि कैसे चीजों को इस मामले में पार्स किए जाते हैं । ऐसा नहीं है (xs) जहां यह सिर्फ एक नाम है।

4

अन्य उत्तरों ने ($) कोष्ठक प्रतिस्थापित कर सकते हैं, इस पर विस्तार से टिप्पणी की है, क्योंकि ($) को सही प्राथमिकता वाले एप्लिकेशन ऑपरेटर के रूप में परिभाषित किया गया था।

मैं जोड़ने के लिए है कि GHC, क्रम में यह संभव ($) साथ कोष्ठक को बदलने के लिए करना चाहते हैं, कुछ और जादू का उपयोग करता है क्या ($) की परिभाषा से देखा जा सकता से हुड के नीचे। जब उच्च पद कार्यों शामिल हैं, प्रकार प्रणाली जब मानक आवेदन के माध्यम से पारित कर दिया (f x के रूप में) उच्च रैंक तर्क के साथ सामना कर सकते हैं, लेकिन हालांकि एक आवेदन ऑपरेटर पारित कर दिया नहीं जब (f $ x के रूप में)। इस समस्या को दूर करने के लिए, जीएचसी टाइप सिस्टम में एक विशेष तरीके से ($) संभालती है। दरअसल, निम्न कोड से पता चलता है कि यदि हम परिभाषित करने और हमारे अपने आवेदन ऑपरेटर ($$) का उपयोग करें, प्रकार प्रणाली एक ही जादू से निपटने लागू नहीं होता।

{-# LANGUAGE RankNTypes #-} 

-- A higher rank function 
higherRank :: (forall a. a -> a -> a) -> (Int, Char) 
higherRank f = (f 3 4, f 'a' 'b') 

-- Standard application 
test0 :: (Int, Char) 
test0 = higherRank const  -- evaluates to (3,'a') 

-- Application via the ($) operator 
test1 :: (Int, Char) 
test1 = higherRank $ const -- again (3, 'a') 

-- A redefinition of ($) 
infixr 0 $$ 
($$) :: (a -> b) -> a -> b 
($$) = ($) 

test2 :: (Int, Char) 
test2 = higherRank $$ const -- Type error (!) 
-- Couldn't match expected type `forall a. a -> a -> a' 
--    with actual type `a0 -> b0 -> a0' 
संबंधित मुद्दे