गार्ड्स बनाम यदि-तब-तब बनाम हास्केल में मामले


104

मेरे पास तीन कार्य हैं जो किसी सूची के nth तत्व को खोजते हैं:

nthElement :: [a] -> Int -> Maybe a 
nthElement [] a = Nothing
nthElement (x:xs) a | a <= 0 = Nothing
                    | a == 1 = Just x
                    | a > 1 = nthElement xs (a-1)

nthElementIf :: [a] -> Int -> Maybe a
nthElementIf [] a = Nothing
nthElementIf (x:xs) a = if a <= 1
                        then if a <= 0 
                             then Nothing
                             else Just x -- a == 1
                        else nthElementIf xs (a-1)                           

nthElementCases :: [a] -> Int -> Maybe a
nthElementCases [] a = Nothing
nthElementCases (x:xs) a = case a <= 0 of
                             True -> Nothing
                             False -> case a == 1 of
                                        True -> Just x
                                        False -> nthElementCases xs (a-1)

मेरी राय में, पहला कार्य सबसे अच्छा कार्यान्वयन है क्योंकि यह सबसे संक्षिप्त है। लेकिन क्या अन्य दो कार्यान्वयन के बारे में कुछ ऐसा है जो उन्हें बेहतर बना देगा? और विस्तार से, आप गार्ड का उपयोग करने के बीच का चयन कैसे करेंगे, यदि-तब-तब बयान, और मामले?


5
आप अपने नेस्टेड संक्षिप्त कर सकते हैं caseबयान करता है, तो आप के लिए इस्तेमाल कियाcase compare a 0 of LT -> ... | EQ -> ... | GT -> ...
rampion

5
@ क्रैम्पियन: आपका मतलब हैcase compare a 1 of ...
नवागत

जवाबों:


121

एक तकनीकी दृष्टिकोण से, तीनों संस्करण समान हैं।

कहा जा रहा है, शैलियों के लिए मेरे अंगूठे का नियम यह है कि यदि आप इसे पढ़ सकते हैं जैसे कि यह अंग्रेजी था ( |"जब" के रूप में पढ़ा जाता है, | otherwise"अन्यथा" के =रूप में और "जैसा है" या "हो"), आप शायद कुछ कर रहे हैं सही।

if..then..elseजब आपके पास एक बाइनरी शर्त है , या एक एकल निर्णय जिसे आपको करने की आवश्यकता है। नेस्से- if..then..elseनेक्सप्रेस, हास्केल में बहुत ही असामान्य हैं, और गार्ड को इसके बजाय लगभग हमेशा इस्तेमाल किया जाना चाहिए।

let absOfN =
  if n < 0 -- Single binary expression
  then -n
  else  n

प्रत्येक if..then..elseअभिव्यक्ति को एक गार्ड द्वारा प्रतिस्थापित किया जा सकता है यदि यह किसी फ़ंक्शन के शीर्ष स्तर पर है, और इसे आम तौर पर पसंद किया जाना चाहिए, क्योंकि आप अधिक मामलों को अधिक आसानी से जोड़ सकते हैं:

abs n
  | n < 0     = -n
  | otherwise =  n

case..ofजब आपके पास कई कोड पथ होते हैं , और प्रत्येक कोड पथ एक मान की संरचना द्वारा निर्देशित होता है , अर्थात पैटर्न मिलान के माध्यम से। आप बहुत ही कम मैच पर Trueऔर False

case mapping of
  Constant v -> const v
  Function f -> map f

गार्ड्स case..ofअभिव्यक्ति के पूरक हैं , जिसका अर्थ है कि यदि आपको एक मूल्य के आधार पर जटिल निर्णय लेने की आवश्यकता है, तो पहले अपने इनपुट की संरचना के आधार पर निर्णय लें, और फिर संरचना में मूल्यों पर निर्णय लें।

handle  ExitSuccess = return ()
handle (ExitFailure code)
  | code < 0  = putStrLn . ("internal error " ++) . show . abs $ code
  | otherwise = putStrLn . ("user error " ++)     . show       $ code

Btw। एक स्टाइल टिप के रूप में, हमेशा एक के बाद =या उससे पहले एक नई |रेखा बनाते हैं यदि =/ के बाद सामान |एक लाइन के लिए बहुत लंबा है, या किसी अन्य कारण से अधिक लाइनों का उपयोग करता है:

-- NO!
nthElement (x:xs) a | a <= 0 = Nothing
                    | a == 1 = Just x
                    | a > 1 = nthElement xs (a-1)

-- Much more compact! Look at those spaces we didn't waste!
nthElement (x:xs) a
  | a <= 0    = Nothing
  | a == 1    = Just x
  | otherwise = nthElement xs (a-1)

1
"आप बहुत ही कम मैच करते हैं Trueऔर False" क्या कोई अवसर है जहाँ आप ऐसा करेंगे? आखिरकार, इस तरह का निर्णय हमेशा ए के साथ किया जा सकता है if, और गार्ड के साथ भी।
लेफ्टरनैबाउट

2
जैसेcase (foo, bar, baz) of (True, False, False) -> ...
dflemstr

@dflemstr वहाँ कोई और अधिक सूक्ष्म अंतर नहीं हैं जैसे कि मोनाडप्लस की आवश्यकता वाले गार्ड और अगर-तब-तब नहीं तो मोनड का एक उदाहरण लौटाएं? किंतु मुझे यकीन नहीं है।
जे फ्रिट्स

2
@JFritsch: guardफ़ंक्शन की आवश्यकता है MonadPlus, लेकिन हम यहां | test =जिस बारे में बात कर रहे हैं, वह क्लॉस के रूप में गार्ड है , जो संबंधित नहीं हैं।
बेन मिलवुड

स्टाइल टिप के लिए धन्यवाद, अब यह संदेह की पुष्टि करता है।
३३ पर truthadjustr

22

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

nthElement xs n = guard (n > 0) >> listToMaybe (drop (n-1) xs)

2

यह केवल आदेश देने की बात है, लेकिन मुझे लगता है कि यह बहुत पठनीय है और गार्ड के समान संरचना है।

nthElement :: [a] -> Int -> Maybe a 
nthElement [] a = Nothing
nthElement (x:xs) a = if a  < 1 then Nothing else
                      if a == 1 then Just x
                      else nthElement xs (a-1)

अंतिम की आवश्यकता नहीं है और अगर कोई अन्य संभावना नहीं है, तो भी कुछ भी याद नहीं होने की स्थिति में भी "अंतिम उपाय मामला" होना चाहिए।


4
यदि आप केस गार्ड का उपयोग कर सकते हैं तो नेस्टेड स्टेटमेंट एक विरोधी पैटर्न है।
user76284
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.