首页 > 解决方案 > 天数之间的计算差异

问题描述

我无法理解这一点。我有以下代码:

module Lib
    ( csvFile
    , analyse
    ) where

import Text.ParserCombinators.Parsec
import Data.Time
import Data.Time.Calendar
import qualified Data.Map as Map

data Item = Item
    { name :: String
    , expire :: Day
    , stock :: Integer
    , price :: Float
    } deriving (Show)


csvFile = endBy line eol
line = sepBy cell (char ';')
cell = quotedCell <|> many (noneOf ";\n\r")

quotedCell =
    do char '"'
       content <- many quotedChar
       char '"' <?> "quote at end of cell"
       return content

quotedChar =
        noneOf "\""
    <|> try (string "\"\"" >> return '"')

eol =   try (string "\n\r")
    <|> try (string "\r\n")
    <|> string "\n"
    <|> string "\r"
    <?> "end of line"

parseDate :: String -> Day
parseDate dateString = parseTimeOrError True defaultTimeLocale "(%Y,%-m,%-d)" dateString :: Day

analyse :: [[String]] -> [Item]
analyse csvData = do
    let items = transform h t
    analyseItems items
        where
           h = head csvData
           t = tail csvData

listToItem :: [String] -> Item
listToItem [] = error "Empty List"
listToItem [n, e, s, p] = do
    let name = n
    let expires = parseDate e
    let stock = read s :: Integer
    let price = read p :: Float
    Item name expires stock price
listToItem _  = error "To few/much Arguments"

transform :: [String] -> [[String]] -> [Item]
transform line [] = do
    let items = []
    let item = listToItem line
    item : items
transform line csvData = do
    let item = listToItem line
    item : (transform h t)
    where
       h = head csvData
       t = tail csvData

analyseItems :: [Item] -> [Item]
analyseItems items = do
    --let sale = getOnSale items
    getExpired (head items) (tail items)

today :: IO Day
today = fmap utctDay getCurrentTime

daysAway :: Day -> IO Integer
daysAway day = fmap (diffDays day) today

getExpired :: item -> [Item] -> [Item]
getExpired item [] = do
    diff <- daysAway (expire item)
    case compare diff 0 of
        LT -> item : []
        GT -> []
        EQ -> []
getExpired item items = do
    diff <- daysAway (expire item)
    case compare diff 0 of
        LT -> item : getExpired h t
        GT -> getExpired h t
        EQ -> getExpired h t
    where
        h = head items
        t = tail items

我用它从 CSV 文件中读取值,其中一个值是一天。我已经设法让这些工作,直到我必须计算今天与项目到期之日的差异。我不知道如何计算这一天。我得到的错误如下:

/home/max/Documents/haskell/Hausaufgabe_02/analysis/src/Lib.hs:85:13: error:
    • Couldn't match type ‘IO’ with ‘[]’
      Expected type: [Integer]
        Actual type: IO Integer
    • In a stmt of a 'do' block: diff <- daysAway (expire item)
      In the expression:
        do diff <- daysAway (expire item)
           case compare diff 0 of
             LT -> item : []
             GT -> []
             EQ -> []
      In an equation for ‘getExpired’:
          getExpired item []
            = do diff <- daysAway (expire item)
                 case compare diff 0 of
                   LT -> item : ...
                   GT -> ...
                   EQ -> ...
   |
85 |     diff <- daysAway (expire item)
   |             ^^^^^^^^^^^^^^^^^^^^^^

/home/max/Documents/haskell/Hausaufgabe_02/analysis/src/Lib.hs:91:13: error:
    • Couldn't match type ‘IO’ with ‘[]’
      Expected type: [Integer]
        Actual type: IO Integer
    • In a stmt of a 'do' block: diff <- daysAway (expire item)
      In the expression:
        do diff <- daysAway (expire item)
           case compare diff 0 of
             LT -> item : getExpired h t
             GT -> getExpired h t
             EQ -> getExpired h t
      In an equation for ‘getExpired’:
          getExpired item items
            = do diff <- daysAway (expire item)
                 case compare diff 0 of
                   LT -> item : getExpired h t
                   GT -> getExpired h t
                   EQ -> getExpired h t
            where
                h = head items
                t = tail items
   |
91 |     diff <- daysAway (expire item)
   |             ^^^^^^^^^^^^^^^^^^^^^^

欢迎任何帮助,因为我必须在今天午夜之前完成这项作业......

标签: csvhaskelltime

解决方案


一个常见的错误是do在没有单子上下文的函数中使用。do这是符号被认为是有害的原因之一[Haskell-wiki]do表达式实际上是语法糖Haskell 报告描述了如何“脱”这些。

对于类似的函数listToItem :: [String] -> Item,你不应该使用do符号。这是行不通的,特别是因为Item它不是一种Monad类型。

例如,我们可以实现listToItem为:

listToItem :: [String] -> Item
listToItem [] = error "Empty List"
listToItem [n, e, s, p] = Item (read n) (parseDate e) (read s) (read p)
listToItem _  = error "To few/much Arguments"

为了计算daysAway,最好将其设为纯函数,并使用Day参数计算差异:

daysAway :: Day -> Day -> Integer
daysAway = flip diffDays

然后analyseItems可以只是filter :: (a -> Bool) -> [a] -> [a]上的项目daysAway

analyseItems :: Day -> [Item] -> [Item]
analyseItems today = filter ((0 >) . daysAway today . expire)

因此,在这里我们可以获得Item在给定 过期的 s列表Day。我们这里根本不需要这个getExpired函数,或者使用递归来过滤。

我们可以使用stransform的行列表:Itemmap :: (a -> b) -> [a] -> [b]

transform :: [[String]] -> [Item]
transform = map listToItem

现在我们可以IO Item获取过期项目,例如:

getExpired :: [Item] -> IO [Item]
getExpired items = fmap (flip analyseItems items) today

我将解析 csv 文件,通过 处理它transform,然后将其过滤getExpired为练习。


推荐阅读