map
- 改變每個元素
fold
- 將所有的元素
scan
- 將所有的元素保持運行的「總」 - 這是你所需要的
它會更容易將所有內容保持爲整數直到最後:
type PosixOffset = Integer
A str在你的listOfTimes
可能是一個unix時間,一個增量或一個錯誤的值。 我們可以通過Maybe (Either PosixOffset Integer)
來表示,但這可能會讓人討厭。 讓我們滾我們自己:
data Time = Unix PosixOffset | Inc Integer | Error String deriving Show
這讓我靈活一下我們有一個錯誤之後做:用error
程序崩潰, 顯示Error
消息給用戶,但不知何故讓他們恢復,或者忽略不良的價值。
讓我們製作安全版本來代替read :: String -> Integer
,它返回Nothing
而不是崩潰。我們需要import Data.Char (isDigit)
readInteger :: String -> Maybe Integer
readInteger "" = Nothing
readInteger xs | all isDigit xs = Just (read xs)
| otherwise = Nothing
現在我們可以用它來readTime
有一些有用的Error
消息。
readTime :: String -> Time
readTime ('u':xs) = case readInteger xs of
Just i -> Unix i
Nothing -> Error $ "readTime: there should be an integer after the u, but I got: " ++ 'u':xs
readTime [] = Error "readTime: empty time"
readTime xs = case readInteger xs of
Just i -> Inc i
Nothing -> Error $ "readTime: " ++ xs ++ " is neither a unix time nor an increment."
的計劃是我們的字符串列表轉換爲對(PosixOffset,Integer)
, 與上次從UNIX時已知PosixOffset
,和當前增量的列表。 我們會再需要能夠給這些成對的轉換爲UTCTime
toUTC :: (PosixOffset,Integer) -> UTCTime
toUTC (p,i) = psUTC (p+i)
現在,我們需要知道如何運行總量的Time
S的下一個Time
結合起來。我們會保留最後的unix時間以供參考。
addTime :: (PosixOffset,Integer) -> Time -> (PosixOffset,Integer)
addTime (oldunix,oldinc) time = case time of
Unix new -> (new,0) -- If there's a new unix time, replace and reset the inc to 0.
Inc inc -> (oldunix,inc) -- If there's a new increment, replace the old one.
Error msg -> error msg -- If there's an error, crash showing it.
或者你可以使用
addTimeTolerant :: (PosixOffset,Integer) -> Time -> (PosixOffset,Integer)
addTimeTolerant (oldunix,oldinc) time = case time of
Unix new -> (new,0) -- If there's a new unix time, replace and reset the inc to 0.
Inc inc -> (oldunix,inc) -- If there's a new increment, replace the old one.
Error msg -> (oldunix,oldinc) -- If there's an error, ignore it and keep the time the same.
現在,我們可以把它粘起來:轉String
s轉換Time
S, 然後將它們通過scan
寧與addTime
結合成(PosixOffset,Integer)
對, 然後關閉所有由此產生的對變爲UTCTime
s。
runningTotal :: [String] -> [UTCTime]
runningTotal [] = []
runningTotal xss = let (t:ts) = map readTime xss in -- turn Strings to Times
case t of
Error msg -> error msg
Inc _ -> error "runningTotal: list must start with a unix time"
Unix po -> map toUTC $ scanl addTime (po,0) ts -- scan the list adding times,
-- starting with an initial unix time
-- then convert them all to UTC
,或者如果你喜歡保持冷靜和進行的addTimeTolerant
方法,你可以使用
isn't_UnixTime :: Time -> Bool
isn't_UnixTime (Unix _) = False
isn't_UnixTime _ = True
runningTotalTolerant :: [String] -> [UTCTime]
runningTotalTolerant xss =
let ts = dropWhile isn't_UnixTime (map readTime xss) in -- cheerily find the first unix time
if null ts then [] else -- if there wasn't one, there are no UTCTimes
let (Unix po) = head ts in -- grab the first time
map toUTC $ scanl addTimeTolerant (po,0) (tail ts) -- scan the list adding times,
-- starting with an initial unix time
-- then convert them all to UTC
你可能想使用的答案中描述的技術,以[「參考了以前更新內容更新函數內的列表「](http://stackoverflow.com/q/12144973/791604)。 –
我認爲這太過分了 - 這是積累 - 掃描的一個是最好的。 – AndrewC