2012-08-31 80 views
2

我有一個字符串列表,字符串是unixtime或從該unixtime增量例如。稍微複雜[字符串] - > [UTCTime]

listOfTimes :: [String] 
listOfTimes = ["u1345469400","1","2","3","4","5","6","u1346427334","1","2","3","4","5","6"] 

我寫這需要unixtime並返回一個UTCTime

dateStringToUTC :: [Char] -> UTCTime 
dateStringToUTC a = out 
    where 
    asInt = read (tail a) :: Integer 
    out = psUTC asInt 

,或採取增量和最後unixtime並返回一個UTCTime

incToUTC :: [Char] -> String -> UTCTime 
incToUTC a b = madeDate 
    where 
    madeDate = psUTC posixOffset 
    posixOffset = lastTime + incTime 
    lastTime = read (tail a) :: Integer 
    incTime = read b :: Integer 

功能,但是我做不到想想一種方法來編寫一個函數,我可以映射整個列表返回一個[UTCTime]

+0

你可能想使用的答案中描述的技術,以[「參考了以前更新內容更新函數內的列表「](http://stackoverflow.com/q/12144973/791604)。 –

+0

我認爲這太過分了 - 這是積累 - 掃描的一個是最好的。 – AndrewC

回答

0

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 
1

這不是一張地圖,因爲你的inc函數有2個參數 - 你在之後的調用中使用了以前的列表元素。看看摺疊:foldlfoldr

4

由於JA的回答說,這不是一個簡單的地圖。一般摺疊可以起作用,但任何列表操作都是如此。

你在這裏想要做的事情聽起來更像是a use for scanr,這是一個產生每個中間步驟列表的正確摺疊,而不僅僅是最終結果。在你的情況下,累加器將是以前的時間值,並且在每一步中,您都可以添加一個增量或將其替換爲新的時間。輸出將是每個計算時間的(懶惰!)列表。

+1

我想你的意思是'scanl'。 –

+0

@DanielWagner:我也認爲當我沒有時間去真正思考我在打字什麼的時候,我可能不應該寫答案......也就是說,呃,是的,你是對的。當我有空時,嘆了口氣,會解決它。 –

0
timesToUnixTimes :: [String] -> [UTCTime] 

由於JA指出,這不是一個簡單的map。但是轉換[Integer][UTCTime]的最後一步是map

timesToUnixTimes (s : ss) = map psUTC (i : is) 
    where 

輸入列表的第一個元素,s,最好是一個unixtime:

i = read (tail s) :: Integer 

後面的元素,ss,可能是,所以解碼功能需要訪問輸出列表的前一個元素:

is = zipWith timeToInteger ss (i : is) 

寫作timeToInteger :: String -> Integer -> Integer留作練習。

兩個從這點:

  1. 你能想到的zipWith作爲同時映射在兩個列表的功能(類似,zipWith3超過三個列表的功能同時對應,zipWith4地圖在四個列表一次等;不存在稱爲zipWith1的功能,因爲它被稱爲map)。

  2. is出現在它自己的定義中。此作品感謝懶惰非嚴格。

    1. is第一元件取決於的ss第一元件上,並且上i
    2. is的第二個元素取決於第二個元素ss和第一個元素is
    3. is的第三個元素取決於ss的第三個元素和is的第二個元素。
    4. 等等

    is沒有元素取決於本身,或is稍後元素。

1

另一種方式是,收集對應於彼此成一個單獨的列表中的時間,並分別處理它們,即

convertUTCs [] = [] 
convertUTCs (x:xs) = map (incToUTC x) increments ++ convertUTCs rest 
    where 
    (increments, rest) = break (\str -> head str == 'u') xs 

這需要第一元件(應該始終是表格"u12345")以及當時的所有增量(即不以'u'開頭的元素),然後對其進行處理。