我是haskell的新手,想通过尝试真实世界的应用程序来接近learngin。其中一个组件是能够将ISO格式的字符串中的日期解析为其组件。 This Stack Overflow post帮助我开始,但这还不够,我很困惑。
我有以下代码:
import System.Locale
import Data.Time
import Data.Time.Format
data IsoDate = IsoDate {
year :: Int
, month :: Int
, day :: Int
} deriving (Show)
parseIsoDate :: String -> IsoDate
parseIsoDate dateString =
IsoDate year month day
where
timeFromString = readTime defaultTimeLocale "%Y %m %d" dateString :: UTCTime
year = 2013
month = 10
day = 31
对于2013年万圣节来说,这很好,很花哨。我很喜欢将年改写为:
year = formatTime defaultTimeLocale "%y" timeFromString
我知道会失败(无法使用IsoDate
构建我的String
类型。然后尝试将字符串读入Int。
year = read (formatTime defaultTimeLocale "%y" timeFromString)
以下回复:
parseIsoDate "2012-12-23"
IsoDate {year = *** Exception: readsTime: bad input "2012-12-23"
还有其他一些尝试让这种转变 - 但我发布的是最理性的尝试,o我不打算发布其他尝试。
我想知道如何使用我当前的代码(因为我正在尝试学习构造),另外(因为日期解析是必不可少的)我想知道更好的方法(也许是最惯用的)在Haskell中处理这个问题。
答案 0 :(得分:1)
这是一个答案:
data IsoDate = IsoDate {
year :: Int
, month :: Int
, day :: Int
} deriving (Show)
parseIsoDate :: String -> IsoDate
parseIsoDate dateString =
IsoDate year month day
where
timeFromString = readTime defaultTimeLocale "%Y %m %d" dateString :: UTCTime
year = read (formatTime defaultTimeLocale "%0Y" timeFromString) :: Int
month = read (formatTime defaultTimeLocale "%m" timeFromString) :: Int
day = read (formatTime defaultTimeLocale "%d" timeFromString) :: Int
对哪些重构:
data DatePart = Year | Month | Day deriving(Enum, Show)
datePart :: DatePart -> UTCTime -> Int
datePart Year utcTime = read (formatTime defaultTimeLocale "%0Y" utcTime)
datePart Month utcTime = read (formatTime defaultTimeLocale "%m" utcTime)
datePart Day utcTime = read (formatTime defaultTimeLocale "%d" utcTime)
parseIsoDate :: String -> IsoDate
parseIsoDate dateString =
IsoDate year month day
where
timeFromString = readTime defaultTimeLocale "%Y %m %d" dateString :: UTCTime
year = datePart Year timeFromString
month = datePart Month timeFromString
day = datePart Day timeFromString
用法
parseIsoDate "2012 12 02"
该数据不是ISO格式,仍在努力让它读取“2012-12-01”。还在寻找使其在语言中工作的首选方式。
更新破折号是微不足道的变化“%Y%m%d”到“%Y-%m-%d”我以为我曾尝试过这个预告片,但它必须与其他代码一起出错。
答案 1 :(得分:1)
我认为你需要做的事情是
parseIsoDate :: String -> Maybe IsoDate
因为并非您提供的每个String
都是有效日期。实现它你已经掌握了大部分成分,但我不认为你想要解析一个UTCTime
而是一个Day
可以转换成你的数据结构。
import Data.Time
data IsoDate = ...
parseIsoDate :: String -> Maybe IsoDate
parseIsoDate str = do julianDay <- parse str
let (y, m, d) = toGregorian julianDay
return $ IsoDate (fromIntegral y) m d
where parse:: String -> Maybe Day
parse = parseTimeM True defaultTimeLocale "%F"
现在有点解释和建议:
我会将数据类型IsoDate
更改为使用Integer
多年 - 因为它们可能很大(至少大于Int
- 只需看看我们宇宙的年龄)。这也是toGregorian
转换Day -> (Integer, Int, Int)
的结果的选择,如果不是,您必须在Integer
的帮助下将其生成的Int
转换为fromIntegral
正如您在我的示例中看到的那样{1}}。
我使用的语法称为Maybe
的 do-syntax ,这在第一行中是一个方便的东西我在monad中提取一个值并且绑定它的名称 - julianDay
。
然后我将值转换为格里高利日。
然后return
再次进入Maybe
。如果第一步失败并产生Nothing
,即String
只是gobbledygook,那么其他任何操作都没有完成,你的程序完成而没有做任何工作(那就是懒惰的评价)。
如果您使用的是RecordWildCards
扩展程序,并且事实可能是Functor
,则可以执行以下操作
{-# LANGUAGE Record
module MyLib
import Data.Time
data IsoDate = IsoDate { year :: Integer
, month :: Int
, day :: Int}
deriving (Show)
parseIsoDate :: String -> Maybe IsoDate
parseIsoDate str = do (year, month, day) <- toGregorian <$> parse str
return IsoDate{..}
where parse:: String -> Maybe Day
parse = parseTimeM True defaultTimeLocale "%F"