हास्केल: जहां बनाम


117

मैं हास्केल के लिए नया हूं और जहां बनाम लेट हूं, वहां बहुत उलझन में हूं । वे दोनों एक समान उद्देश्य प्रदान करते हैं। मैंने जहां बनाम लेट के बीच कुछ तुलनाएं पढ़ी हैं, लेकिन प्रत्येक का उपयोग करते समय मुझे समझदारी से परेशानी हो रही है। क्या कोई कृपया कुछ संदर्भ या शायद कुछ उदाहरण प्रदान कर सकता है जो प्रदर्शित करता है कि कब एक का उपयोग करना है?

जहां बनाम

एक whereखंड को केवल एक फ़ंक्शन परिभाषा के स्तर पर परिभाषित किया जा सकता है। आमतौर पर, यह letपरिभाषा के दायरे के समान है । अंतर केवल तब है जब गार्ड्स का इस्तेमाल किया जा रहा हैwhereक्लॉज का दायरा सभी गार्डों पर फैला हुआ है। इसके विपरीत, एक letअभिव्यक्ति का दायरा केवल वर्तमान फ़ंक्शन क्लॉज और गार्ड है, यदि कोई हो।

हास्केल धोखा शीट

हास्केल विकी बहुत विस्तृत है और विभिन्न मामलों प्रदान करता है, लेकिन यह काल्पनिक उदाहरण का उपयोग करता है। मुझे इसकी व्याख्याएँ एक शुरुआत के लिए बहुत संक्षिप्त लगती हैं।

लेट के लाभ :

f :: State s a
f = State $ \x -> y
   where y = ... x ...

Control.Monad.State

काम नहीं करेगा, क्योंकि जहां पैटर्न मिलान f = को संदर्भित करता है, जहां कोई एक्स गुंजाइश नहीं है। इसके विपरीत, अगर आपने शुरुआत की थी, तो आपको परेशानी नहीं होगी।

हास्केल विकी ऑन लेट के फायदे

f :: State s a
f = State $ \x ->
   let y = ... x ...
   in  y

कहाँ के लाभ :

f x
  | cond1 x   = a
  | cond2 x   = g a
  | otherwise = f (h x a)
  where
    a = w x

f x
  = let a = w x
    in case () of
        _ | cond1 x   = a
          | cond2 x   = g a
          | otherwise = f (h x a)

घोषणा बनाम अभिव्यक्ति

हास्केल विकी का उल्लेख है कि लेट अभिव्यक्ति अभिव्यंजक है , जहां क्लॉज घोषणात्मक है। शैली के अलावा वे अलग तरह से कैसे प्रदर्शन करते हैं?

Declaration style                     | Expression-style
--------------------------------------+---------------------------------------------
where clause                          | let expression
arguments LHS:     f x = x*x          | Lambda abstraction: f = \x -> x*x
Pattern matching:  f [] = 0           | case expression:    f xs = case xs of [] -> 0
Guards:            f [x] | x>0 = 'a'  | if expression:      f [x] = if x>0 then 'a' else ...
  1. पहले उदाहरण में क्यों है चलो दायरे में लेकिन कहाँ नहीं है?
  2. क्या पहला उदाहरण कहां लागू करना संभव है ?
  3. क्या कुछ इसे वास्तविक उदाहरणों पर लागू कर सकते हैं जहां चर वास्तविक अभिव्यक्तियों का प्रतिनिधित्व करते हैं?
  4. क्या प्रत्येक का उपयोग करने के लिए अंगूठे का एक सामान्य नियम है?

अपडेट करें

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

भाव व्यक्त करते हैं।

जब भी बाइंडिंग के एक नेस्टेड सेट की आवश्यकता होती है, तो हास्केल के दो भाव उपयोगी होते हैं। एक साधारण उदाहरण के रूप में, विचार करें:

let y   = a*b
    f x = (x+y)/y
in f c + f d

लेट एक्सप्रेशन द्वारा बनाई गई बाइंडिंग का सेट परस्पर पुनरावर्ती है, और पैटर्न बाइंडिंग को आलसी पैटर्न के रूप में माना जाता है (यानी वे एक निहित ~ ले जाते हैं)। अनुमति के एकमात्र प्रकार प्रकार हस्ताक्षर, फ़ंक्शन बाइंडिंग और पैटर्न बाइंडिंग हैं।

जहां क्लॉस है।

कभी-कभी कई संरक्षित समीकरणों पर बाइंडिंग को गुंजाइश देना सुविधाजनक होता है, जिसके लिए एक खंड की आवश्यकता होती है:

f x y  |  y>z           =  ...
       |  y==z          =  ...
       |  y<z           =  ...
     where z = x*x

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


9
मेरे बीच के अंतर से हैरान था letऔर whereजब मैंने पहली बार हास्केल सीखना शुरू किया। मुझे लगता है कि इसे समझने का सबसे अच्छा तरीका यह है कि यह महसूस किया जाए कि दोनों के बीच बहुत कम अंतर है, और इसलिए चिंता की कोई बात नहीं है। एक बहुत ही सरल यांत्रिक परिवर्तन whereके letमाध्यम से दिए गए अर्थ । Haskell.org/onlinereport/decls.html#sect4.4.3.2 देखें यह परिवर्तन केवल वाजिब सुविधा के लिए मौजूद है।
टॉम एलिस

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

@ टॉम एलिस, टॉम, मैं आपके द्वारा बताए गए लिंक को समझने की कोशिश कर रहा था, लेकिन यह मेरे लिए बहुत मुश्किल था, क्या आप इस साधारण परिवर्तन को केवल नश्वर लोगों के लिए समझा सकते हैं?
jhegedus

1
@ ज़ेगेदस: का f = body where x = xbody; y = ybody ...अर्थ हैf = let x = xbody; y = ybody ... in body
टॉम एलिस

धन्यवाद टॉम! क्या यह दूसरे रास्ते से जा सकता है? क्या case .... of ... whereकिसी अभिव्यक्ति को किसी भी तरह से अभिव्यक्ति में बदलना संभव है ? मैं इस बारे में निश्चित नहीं हूँ।
jhegedus

जवाबों:


39

1: उदाहरण में समस्या

f :: State s a
f = State $ \x -> y
    where y = ... x ...

पैरामीटर है xwhereक्लॉज की चीजें केवल फ़ंक्शन के मापदंडों f(कोई भी नहीं हैं) और बाहरी स्कोप की चीजों को संदर्भित कर सकती हैं ।

2: whereपहले उदाहरण में एक का उपयोग करने के लिए, आप एक दूसरे नामांकित फ़ंक्शन को पेश कर सकते हैं xजो इस तरह के पैरामीटर के रूप में लेता है :

f = State f'
f' x = y
    where y = ... x ...

या इस तरह:

f = State f'
    where
    f' x = y
        where y = ... x ...

3: यहाँ एक पूर्ण उदाहरण बिना है ...:

module StateExample where

data State a s = State (s -> (a, s))

f1 :: State Int (Int, Int)
f1 = State $ \state@(a, b) ->
    let
        hypot = a^2 + b^2
        result = (hypot, state)
    in result

f2 :: State Int (Int, Int)
f2 = State f
    where
    f state@(a, b) = result
        where
        hypot = a^2 + b^2
        result = (hypot, state)

4: जब उपयोग करने के लिए letया whereस्वाद की बात है। मैं letएक संगणना (सामने की ओर ले जाकर) और whereकार्यक्रम के प्रवाह (पीछे की ओर अभिकलन को स्थानांतरित करके ) पर जोर देने के लिए उपयोग करता हूं ।


2
"चीजें जहां क्लॉज केवल फ़ंक्शन के मापदंडों को संदर्भित कर सकता है f (बाहरी कोई नहीं हैं) और बाहरी स्कोप में चीजें।" - जो वास्तव में मेरे लिए इसे स्पष्ट करने में मदद करता है।

28

हालांकि, गार्ड के संबंध में तकनीकी अंतर है जो कि विशेष रूप से बताया गया है, इसमें एक वैचारिक अंतर भी है कि क्या आप मुख्य सूत्र को अतिरिक्त परिभाषित चर के साथ ऊपर रखना चाहते हैं ( where) या क्या आप सब कुछ परिभाषित करना चाहते हैं और सूत्र डाल सकते हैं नीचे ( let)। प्रत्येक शैली का एक अलग जोर होता है और आप गणित पेपर, पाठ्यपुस्तक आदि में दोनों का उपयोग करते हुए देखते हैं। आम तौर पर, चर जो पर्याप्त रूप से अनपेक्षित होते हैं कि सूत्र उनके बिना समझ में नहीं आता है उन्हें ऊपर परिभाषित किया जाना चाहिए; वे चर जो संदर्भ के कारण सहज हैं या उनके नाम नीचे परिभाषित किए जाने चाहिए। उदाहरण के लिए, ephemient के hasVowel उदाहरण में, का अर्थ vowelsस्पष्ट है और इसलिए इसे इसके उपयोग के ऊपर परिभाषित नहीं किया जाना चाहिए (इस तथ्य की उपेक्षा letकरना कि गार्ड के कारण काम नहीं करेगा)।


1
यह अंगूठे का एक अच्छा नियम प्रदान करता है। क्या आप इस बात पर विस्तार से बता सकते हैं कि ऐसा करने की गुंजाइश अलग क्यों है?

क्योंकि हास्केल सिंटेक्स ऐसा कहता है। क्षमा करें, एक अच्छा उत्तर नहीं है। हो सकता है कि टॉप-स्कॉप्ड परिभाषाओं को पढ़ने के लिए कठिन हो जब "लेट" के तहत टक किया गया था इसलिए इसे अस्वीकृत कर दिया गया था।
gdj

13

कानूनी:

main = print (1 + (let i = 10 in 2 * i + 1))

कानूनी नहीं:

main = print (1 + (2 * i + 1 where i = 10))

कानूनी:

hasVowel [] = False
hasVowel (x:xs)
  | x `elem` vowels = True
  | otherwise = False
  where vowels = "AEIOUaeiou"

कानूनी नहीं: (एमएल के विपरीत)

let vowels = "AEIOUaeiou"
in hasVowel = ...

13
क्या आप बता सकते हैं कि निम्नलिखित उदाहरण मान्य क्यों हैं जबकि दूसरा नहीं है?

2
उन लोगों के लिए जो नहीं जानते, यह कानूनी है: hasVowel = let^M vowels = "AEIOUaeiou"^M in ...( ^Mnewline है)
थॉमस ईडिंग

5

मुझे LYHFGG से यह उदाहरण मददगार लगा:

ghci> 4 * (let a = 9 in a + 1) + 2  
42  

letएक अभिव्यक्ति है तो आप let कहीं भी (!) रख सकते हैं जहाँ भाव जा सकते हैं।

दूसरे शब्दों में, ऊपर दिए गए उदाहरण में इसे केवल बदलने के लिए उपयोग करना संभव नहीं है (शायद कुछ और क्रिया अभिव्यक्ति का उपयोग करके )।whereletcasewhere


3

अफसोस की बात है कि यहां अधिकांश उत्तर एक शुरुआत के लिए बहुत तकनीकी हैं।

LHYFGG का इस पर एक प्रासंगिक अध्याय है, जो आपको पढ़ना चाहिए अगर आप पहले से ही नहीं है, लेकिन संक्षेप में:

  • whereकेवल एक वाक्य रचना है ( चीनी नहीं ) जो केवल फ़ंक्शन परिभाषाओं में उपयोगी है ।
  • let ... inएक अभिव्यक्ति ही है , इस प्रकार आप उनका उपयोग कर सकते हैं जहाँ भी आप एक अभिव्यक्ति रख सकते हैं। स्वयं एक अभिव्यक्ति होने के नाते, यह गार्ड के लिए बाध्यकारी चीजों के लिए इस्तेमाल नहीं किया जा सकता है।

अंत में, आप letसूची बोध में भी उपयोग कर सकते हैं :

calcBmis :: (RealFloat a) => [(a, a)] -> [a]
calcBmis xs = [bmi | (w, h) <- xs, let bmi = w / h ^ 2, bmi >= 25.0]
-- w: width
-- h: height

हम एक में शामिल देना बहुत पसंद है कि हम एक विधेय, केवल यह सूची को फ़िल्टर नहीं करता होगा, यह केवल नाम के लिए बांधता एक सूची समझ अंदर। एक सूची समझ के अंदर लेट में परिभाषित नाम आउटपुट फंक्शन (पार्ट से पहले वाला |) और बाइंडिंग के बाद आने वाले सभी विधेय और अनुभागों के लिए दिखाई देते हैं। इसलिए हम अपने फंक्शन को केवल लोगों के बीएमआई> = 25 पर वापस ला सकते हैं:


यह एकमात्र उत्तर है जिसने वास्तव में मुझे अंतर को समझने में मदद की। जबकि तकनीकी उत्तर अधिक अनुभवी हास्केल-एर के लिए उपयोगी हो सकते हैं, यह मेरे जैसे शुरुआती के लिए पर्याप्त है! +1
Zac G
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.