天之间的计算差异

时间:2019-06-16 12:42:50

标签: csv haskell time

我无法解决这个问题。我有以下代码:

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文件中读取值,其中一个值是Day。我已经设法使这些工作正常进行,直到我必须计算出今天到该物料到期之日的差额为止。我不知道如何计算日。我收到的错误如下:

/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)
   |             ^^^^^^^^^^^^^^^^^^^^^^

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

1 个答案:

答案 0 :(得分:1)

一个常见的错误是在没有Monadic上下文的函数中使用do。这是do notation is considered harmful [Haskell-wiki]的原因之一。 do表达式实际上是语法糖Haskell report描述了如何“ 去糖”。

对于类似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到期的Day个列表。我们根本不需要getExpired函数,也不需要使用递归进行过滤。

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

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

现在我们可以制作一个IO Item来获取过期的物品,例如:

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

我保留解析csv文件,通过transform处理它,然后作为练习使用getExpired对其进行过滤。