2015-12-07 4 views
5

गणित अभिव्यक्तियों को पार्स करने के लिए कोड लिखने का प्रयास करते हुए हास्केल नौसिखिया। कोड:क्या हास्केल में गार्ड घोंसला करना संभव है?

isDigit :: Char -> Bool 
isDigit c = c >= '0' && c <= '9' 

parseNumber :: String -> Maybe (String, String) 
parseNumber [] = Just ("", "") 
parseNumber (h:ls) 
    | isDigit h 
     | p == Nothing = Just([h], ls)  -- Digit found <<< ERROR!! 
     | otherwise = Just (h:fst d, snd d) -- Ends in a digit 
    | h == '.' 
     | p == Nothing = Nothing        -- Ends in a point 
     | not ('.' `elem` (snd d)) = Just (h:(fst d), snd d) -- We don't want multiple dots 
    | otherwise = Nothing  -- Not a number, stop looking! 
    where 
     p = parseNumber ls 
     Just d = parseNumber ls -- Float version of p. Not used if p is Nothing 

यह समारोह एक स्ट्रिंग है एक संख्या के साथ शुरू होता है, और रिटर्न संख्या अभिव्यक्ति के बाकी हिस्सों से अलग कर लेने के लिए माना जाता है। उदाहरण:

parseNumber "123.0 + 2"

("123.0", "+ 2")

मैं इस नेस्टेड गार्ड 'वाक्य रचना वास्तव में अच्छी तरह से पढ़ता है लगता है, लेकिन यह नहीं है काम। त्रुटि रेखा के लिए,

इनपुट पर पार्स त्रुटि '|'

हैंडेल में जंजीर रक्षकों की अनुमति नहीं है? या मैं इसे गलत तरीके से लिख रहा हूं? इसके अलावा, मेरे पास चेन लॉजिक को सरल तरीके से कौन से विकल्प हैं?

उत्तर

11

नहीं है, लेकिन आप मामलों का उपयोग करें यदि आप चाहें तो कर सकते हैं:

parseNumber :: String -> Maybe (String, String) 
parseNumber [] = Just ("", "") 
parseNumber (h:ls) 
    | isDigit h = 
     case() of 
      () | p == Nothing = Just([h], ls) 
       | otherwise = Just (h:fst d, snd d) -- Ends in a digit 
    | h == '.' = 
     case() of 
      () | p == Nothing = Nothing 
       | not ('.' `elem` (snd d)) = Just (h:(fst d), snd d) 
    | otherwise = Nothing 
    where 
     p  = parseNumber ls 
     Just d = parseNumber ls 

वैकल्पिक रूप से, बहु तरफ़ा अगर एक समान तरीके से (if True | p1 -> b ; | p2 -> c) में काम करता है।

3

नहीं, यह संभव नहीं है। क्यों नहीं बस के रूप में

isDigit :: Char -> Bool 
isDigit c = c >= '0' && c <= '9' 

parseNumber :: String -> Maybe (String, String) 
parseNumber [] = Just ("", "") 
parseNumber (h:ls) 
    -- Digit found 
    | isDigit h && p == Nothing = Just([h], ls) 
    -- Ends in a digit 
    | isDigit h = Just (h:fst d, snd d) 
    -- Ends in a point 
    | h == '.' && p == Nothing = Nothing 
    -- We don't want multiple dots 
    | h == '.' && not ('.' `elem` (snd d)) = Just (h:(fst d), snd d) 
    -- Not a number, stop looking! 
    | otherwise = Nothing 
    where 
     p = parseNumber ls 
     Just d = parseNumber ls -- Float version of p. Not used if p is Nothing 

main = print $ parseNumber "123.0 + 2" 

रैखिक बारे में अपने गार्ड भी शामिल यह शायद एक संकेत है कि आप एक समारोह को निकालने के लिए की जरूरत है हो जाते हैं।

+0

धन्यवाद, यह पूरी तरह से काम करता है। मुझे बस पढ़ने के लिए थोड़ा मुश्किल लगता है। – SlySherZ

5

अपने कार्य बेहद जटिल हो जाता है और आप तर्क जो सिर्फ अकेले की रक्षा करता है के साथ लागू किया जाता है का समर्थन नहीं कर सकते हैं, बजाय सार नियंत्रण कार्यों के साथ समारोह लिखने पर विचार:

import Control.Applicative 
import Control.Monad 

isDigit :: Char -> Bool 
isDigit c = c >= '0' && c <= '9' 

parseNumber :: String -> Maybe (String, String) 
parseNumber [] = return ("", "") 
parseNumber (h:ls) = dig <|> dot where -- h is either a digit or a dot 
    p = parseNumber ls 
    dig = do 
    guard (isDigit h)         -- ensure h is a digit 
    fmap (\(ds,r) -> (h:ds,r)) p 
     <|> return ([h],ls) -- the alternative between two computations 
          -- either the tail is parsed and h prepended to the result 
          -- or the digit is returned by itself       
    dot = do 
    guard (h == '.')    -- ensure h is a dot 
    (ds,r) <- p     -- parse the tail 
    guard $ not $ '.' `elem` ds -- ensure there is no dot in the tail 
    return (h:ds,r)    -- result 

इस का उपयोग करता है Monad, Functor, और MonadPlus पार्सिंग तर्क को लागू करने के लिए Maybe के उदाहरण। वास्तव में, यह फ़ंक्शन MonadPlus m => String -> m (String, String) प्रकार को सामान्यीकृत करता है - यहां Maybe रचनाकारों का कोई वास्तविक उपयोग नहीं है।

फ़ंक्शन को पढ़ने में भी आसान है। गार्ड के साथ संस्करण में यह हो रहा है कि यह और भी स्पष्ट है।

+0

यह वह दृष्टिकोण है जिसे मैं सुझाव देना चाहता था। एक ऐसा अवलोकन है जो मुझे लगता है कि उत्तर निकाय में बनाने लायक है, हालांकि: इस मामले में, 'dig <|> डॉट' में परिवर्तन ठीक है क्योंकि 'dig' और 'dot' परस्पर अनन्य गार्ड के साथ शुरू होता है, लेकिन आम तौर पर वे नहीं कर सकते हैं और फिर 'dig' से 'dot' तक गिरने से अवांछित हो सकता है। इसे _ 'निर्माण के' मामले() के मल्टीलाइन के साथ संभाला जा सकता है, हालांकि सिंटैक्स थोड़ी बेकार है जब इसकी आवश्यकता नहीं है (यहां की तरह)। –

+0

@DanielWagner यहां तक ​​कि अगर गार्ड पारस्परिक रूप से अनन्य नहीं हैं, तो उनमें से एक पहले आ जाएगा, इसलिए इसे 'guard1 <|> guard2' में बदलना अभी भी मेरे लिए मान्य लगता है। – user2407038

+0

यदि गार्ड * पारस्परिक रूप से अनन्य नहीं हैं, और 'p' विफल हो जाते हैं लेकिन' dot' सफल होता है, 'dig <|> dot' सफल होगा जहां मूल' dot' को कोशिश किए बिना विफल होगा। –

3

के साथ चेन गार्ड के लिए संभव है। यह मूल रूप से && फ़र्जरी के उत्तर में समान होता है, लेकिन जब यह पैटर्न गार्ड की बात आती है तो यह अधिक बहुमुखी है।

घोंसला गार्ड्स संभव नहीं है। खैर, आपके उदाहरण में केवल पहले खंड में वास्तव में इसकी आवश्यकता है। आप

parseNumber (h:ls) 
    | isDigit h 
     = if isNothing p 
      then Just ([h], ls)  -- Digit found <<< ERROR!! 
      else Just (h:fst d, snd d) -- Ends in a digit 
    | h == '.' 
    , not ('.' `elem` snd d) 
     = Just (h:fst d, snd d) -- We don't want multiple dots 
    | otherwise = Nothing  -- Not a number, stop looking! 
1

का उपयोग where Just d = ... खतरनाक है लिख सकते हैं: अगर आप कभी भी उस तक पहुँच जब p है Nothing अपने पूरे कार्यक्रम दुर्घटना होगा।ऐसा करने से, आपको अपने कोड में ऐसे चेक जोड़ना होगा (जैसा आपने सही ढंग से किया है), और सावधान रहें कि इनमें से किसी एक को न भूलें।

का उपयोग करके maybe एलिमिनेटर का उपयोग करके, या मज़ेदार/आवेदक/मोनैड टूल का उपयोग करके सुरक्षित तरीके हैं। के case का उपयोग यह सरल रखने के लिए करते हैं:

parseNumber :: String -> Maybe (String, String) 
parseNumber [] = Just ("", "") 
parseNumber (h:ls) 
    | isDigit h = case p of 
     Nothing -> Just([h], ls)   -- Digit found <<< ERROR!! 
     Just d -> Just (h:fst d, snd d) -- Ends in a digit 
    | h == '.' = case p of 
     Nothing -> Nothing     -- Ends in a point 
     Just d | not ('.' `elem` (snd d)) 
       -> Just (h:(fst d), snd d) -- We don't want multiple dots 
     _  -> Nothing     -- Not a number, stop looking! 
    where 
     p = parseNumber ls 

हम भी कर सकते हैं d की उप-घटक पर सीधे पैटर्न मैच:

parseNumber :: String -> Maybe (String, String) 
parseNumber [] = Just ("", "") 
parseNumber (h:ls) 
    | isDigit h = case p of 
     Nothing  -> Just([h], ls)  -- Digit found <<< ERROR!! 
     Just (hs,rest) -> Just (h:hs, rest) -- Ends in a digit 
    | h == '.' = case p of 
     Nothing -> Nothing   -- Ends in a point 
     Just (hs, rest) | not ('.' `elem` rest) 
       -> Just (h:hs, rest) -- We don't want multiple dots 
     _  -> Nothing   -- Not a number, stop looking! 
    where 
     p = parseNumber ls 
11

नहीं, आप नहीं कर सकते हैं। हम सभी इसे चाहते हैं, लेकिन कोई भी समझदार वाक्यविन्यास के साथ नहीं आ सकता है।

0

उन्हें अलग-अलग कार्यों में रखें।

isDigit :: Char -> Bool 
isDigit c = c >= '0' && c <= '9' 


parseNumber :: String -> Maybe (String, String) 
parseNumber [] = Just ("", "") 
parseNumber (h:ls) 
    | isDigit h = f_p (h:ls)    
    | h == '.' = temp (h: ls)   
    | otherwise = Nothing  -- Not a number, stop looking! 


f_p :: String -> Maybe (String, String) 
f_p (h:ls) 
    | parseNumber ls == Nothing = Just([h], ls)   -- Digit found <<< ERROR!! 
    | otherwise     = Just (h:fst d, snd d) -- Ends in a digit 
    where 
     Just d = parseNumber ls -- Float version of p. Not used if p is Nothing 


temp :: String -> Maybe (String, String) 
temp (h:ls) 
    | parseNumber ls == Nothing = Nothing     -- Ends in a point 
    | not ('.' `elem` (snd d)) = Just (h:(fst d), snd d) -- We don't want multiple dots 
    where 
     Just d = parseNumber ls -- Float version of p. Not used if p is Nothing 

स्वीकार करना है कि मैंने इस कोड का परीक्षण नहीं किया है।

1

हाल GHC अब MultiWayIf है:

{-# LANGUAGE MultiWayIf #-} 

parseNumber :: String -> Maybe (String, String) 
parseNumber [] = Just ("", "") 
parseNumber (h:ls) 
    | isDigit h = if 
    | p == Nothing -> Just ([h], ls) 
    | otherwise -> Just (h:fst d, snd d) 
    | h == '.' = if 
    | p == Nothing    -> Nothing 
    | not ('.' `elem` (snd d)) -> Just (h:(fst d), snd d) 
    | otherwise = Nothing 
    where [email protected](~(Just d)) = parseNumber ls 

लेकिन यह बेहतर अलग ढंग से किसी भी तरह से थोड़ा लिखा है पक्षपात के बिना,।

{-# LANGUAGE MultiWayIf #-} 

parseNumber :: String -> Maybe (String, String) 
parseNumber [] = Just ("", "") 
parseNumber (h:ls) 
    | isDigit h = if 
    | Nothing <- p -> Just ([h], ls) -- PatternGuards, on by default 
    | Just d <- p -> Just (h:fst d, snd d) 
    | h == '.' = if 
    | Nothing <- p       -> Nothing 
    | Just d <- p, not ('.' `elem` snd d) -> Just (h:(fst d), snd d) 
    | otherwise = Nothing 
    where p = parseNumber ls 

और आप maybe का भी उपयोग कर सकते हैं।

parseNumber :: String -> Maybe (String, String) 
parseNumber "" = Just ("", "") 
parseNumber (h:hs) 
    | isDigit h = maybe (Just ([h], hs)) (\(num, rest') -> Just (h:num, rest')) rest 
    | h == '.' = maybe Nothing (\(num, rest') -> if '.' `elem` num then Nothing 
               else Just (h:num, rest') 
          ) rest -- This logic is a bit wonky; it doesn't really work 
    | otherwise = Nothing 
    where rest = parseNumber hs 
संबंधित मुद्दे