मैं isDigit
का उपयोग करूंगा और समय की अपनी परिभाषा रखूंगा।
import Data.Char (isDigit)
data Time = Time {hour :: Int,
minute :: Int
}
आप थे, लेकिन newTime
को परिभाषित नहीं किया, इसलिए मैं एक अपने आप तो मेरे कोड को संकलित करता लिखा है!
newTime :: Int -> Int -> Time
newTime h m | between 0 23 h && between 0 59 m = Time h m
| otherwise = error "newTime: hours must be in range 0-23 and minutes 0-59"
where between low high val = low <= val && val <= high
सबसे पहले, अपने शो उदाहरण एक छोटे से गलत है क्योंकि show $ Time 10 10
देता "010:010"
instance Show Time where
show (Time hour minute) = (if hour > 9 -- oops
then (show hour)
else ("0" ++ show hour))
++ ":" ++
(if minute > 9 -- oops
then (show minute)
else ("0" ++ show minute))
चलो readsPrec
पर एक नजर है है:
*Main> :i readsPrec
class Read a where
readsPrec :: Int -> ReadS a
...
-- Defined in GHC.Read
*Main> :i ReadS
type ReadS a = String -> [(a, String)]
-- Defined in Text.ParserCombinators.ReadP
एक पार्सर है कि - यह होना चाहिए केवलकी बजाय बेजोड़ शेष स्ट्रिंग को वापस करें, तो तुम सही हो ""
गलत है:
*Main> read "03:22" :: Time
03:22
*Main> read "[23:34,23:12,03:22]" :: [Time]
*** Exception: Prelude.read: no parse
यह पार्स नहीं कर सकता है क्योंकि आप पहले पढ़ने में ,23:12,03:22]
फेंक दिया।
आइए refactor कि थोड़ा इनपुट खाने के लिए के रूप में हम साथ जाने:
instance Read Time where
readsPrec _ input =
let (hours,rest1) = span isDigit input
hour = read hours :: Int
(c:rest2) = rest1
(mins,rest3) = splitAt 2 rest2
minute = read mins :: Int
in
if c==':' && all isDigit mins && length mins == 2 then -- it looks valid
[(newTime hour minute,rest3)]
else [] -- don't give any parse if it was invalid
उदाहरण
Main> read "[23:34,23:12,03:22]" :: [Time]
[23:34,23:12,03:22]
*Main> read "34:76" :: Time
*** Exception: Prelude.read: no parse
के लिए देता है यह, हालांकि, "03:45" अनुमति नहीं है और के रूप में यह व्याख्या "03:45"। मुझे यकीन नहीं है कि यह एक अच्छा विचार है, इसलिए शायद हम एक और परीक्षण length hours == 2
जोड़ सकते हैं।
मैं यह सब विभाजन और अवधि सामान बंद जा रहा हूँ अगर हम इसे इस तरह से कर रहे हैं, तो शायद मैं पसंद करेंगे:
instance Read Time where
readsPrec _ (h1:h2:':':m1:m2:therest) =
let hour = read [h1,h2] :: Int -- lazily doesn't get evaluated unless valid
minute = read [m1,m2] :: Int
in
if all isDigit [h1,h2,m1,m2] then -- it looks valid
[(newTime hour minute,therest)]
else [] -- don't give any parse if it was invalid
readsPrec _ _ = [] -- don't give any parse if it was invalid
वास्तव में क्लीनर और मेरे लिए आसान लगता है कौन सा।
इस समय यह "3:45"
अनुमति नहीं देता:
*Main> read "3:40" :: Time
*** Exception: Prelude.read: no parse
*Main> read "03:40" :: Time
03:40
*Main> read "[03:40,02:10]" :: [Time]
[03:40,02:10]
शो में एक-एक-एक त्रुटि को इंगित करने के लिए धन्यवाद। यह नया कोड अधिक समझ में आता है, और यदि वास्तव में (जिसे मैं उल्लेख करना भूल गया) के लिए अधिक सही होता, तो "स्मार्ट कन्स्ट्रक्टर" 'newTime' पहले से ही वैधता जांच करता है। – Magnap
@Magnap मैंने अनावश्यक चेक संपादित किए हैं और नए समय का उपयोग किया है। – AndrewC
@Magnap मुझे अभी भी यह जांचना होगा कि 'Int' पर 'read' को कॉल करने से पहले मुझे अंक मिल गए हैं। – AndrewC