我无法解决这个问题。我有以下代码:
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)
| ^^^^^^^^^^^^^^^^^^^^^^
欢迎任何帮助,因为我必须在今天午夜之前完成此作业……
答案 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]
transform
到Item
的行列表:
transform :: [[String]] -> [Item]
transform = map listToItem
现在我们可以制作一个IO Item
来获取过期的物品,例如:
getExpired :: [Item] -> IO [Item]
getExpired items = fmap (flip analyseItems items) today
我保留解析csv文件,通过transform
处理它,然后作为练习使用getExpired
对其进行过滤。