2

文字列のリストがあります。文字列はunixtimeか、そのunixtimeからの増分です。

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

または、インクリメントして最後のUNIX時間を取得し、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]を返すリスト全体にマップできる関数を作成する方法を考えることはできません。

4

5 に答える 5

4

jaの答えが言うように、これは単純な地図ではありません。一般的な折り畳みは機能しますが、それはどのリスト操作にも当てはまります。

ここで実行しようとしていることは、より具体的にはの使用法の scanrように聞こえます。これは、最終結果だけでなく、各中間ステップのリストを生成する右折りです。あなたの場合、アキュムレータは前の時間値になり、各ステップで増分を追加するか、新しい時間に置き換えます。出力は、計算された各時間の(怠惰な!)リストになります。

于 2012-08-31T16:47:36.027 に答える
1

inc関数には2つのパラメーターがあるため、これはマップではありません。後続の呼び出しで前のリスト要素を使用しています。折り目を調べます:、、foldlなどfoldr

于 2012-08-31T16:06:47.647 に答える
1

別の方法は、互いに対応する時間を別々のリストに収集し、それらを別々に処理することです。

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

これは、最初の要素(常に形式である必要があります"u12345")とその時間のすべての増分(つまり、で始まらない要素'u')を取得し、それらに対して処理を実行します。

于 2012-08-31T18:28:18.147 に答える
0
timesToUnixTimes :: [String] -> [UTCTime]

jaが指摘するように、これは単純ではありませんmap[Integer]しかし、aをaに変換する最後のステップは[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ことは練習として残されます。

これからの2つのポイント:

  1. 関数を一度に2つのリストにマッピングすることと考えることができますzipWith(同様にzipWith3、関数を一度に3つのリストにマッピングする、一度に4つのリストにマッピングするなど、呼び出されるためにzipWith4呼び出される関数はありません)。zipWith1map

  2. is独自の定義で表示されます。これは、怠惰の非厳密さのおかげで機能します。

    1. の最初の要素は、の最初の要素とにis依存しssますi
    2. の2番目の要素は、の2番目の要素との最初の要素にis依存します。ssis
    3. の3番目の要素は、の3番目の要素との2番目の要素にis依存します。ssis
    4. 等。

    isの要素は、それ自体、または後の。の要素に依存しませんis

于 2012-08-31T16:50:02.203 に答える
0

map-各要素を変更します

fold-すべての要素を組み合わせる

scan-実行中の「合計」を維持するすべての要素を組み合わせる-これはあなたが必要とするものです

最後まですべてを整数として保持する方が簡単です。

type PosixOffset = Integer

の文字列は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)。最後PosixOffsetの情報はUNIX時間からのものであり、現在の増分です。次に、これらのペアをに変換できるようにする必要がありますUTCTime

toUTC :: (PosixOffset,Integer) -> UTCTime
toUTC (p,i) = psUTC (p+i)

Time次に、の現在の合計を次のの合計と組み合わせる方法を知る必要があります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.

これで、一緒に貼り付けることができます。sをsStringに変換し、を使用してそれらをペアTimeに結合し、結果のすべてのペアをsに変換します。(PosixOffset,Integer)scanaddTimeUTCTime

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
于 2012-09-01T15:59:47.780 に答える