Haskell中如何优雅将Text列表转换为自定义Record类型
首先你的朴素实现实际上存在隐含的类型错误:l !! 2是Text类型,而read函数的入参要求是String,需要先用unpack把Text转成String才能正常编译。
下面是符合Haskell风格的两种实现方案:
方案1:满足已知前置条件的最简实现
既然你已经明确输入列表长度固定为3、第三个元素一定可以转成Int,直接用模式匹配解构列表即可,完全避免不安全的!!索引操作,逻辑更清晰:
{-# LANGUAGE RecordWildCards #-} import Data.Text (Text, unpack) readText :: [Text] -> MyRecord readText [rOne, rTwo, rThreeTxt] = MyRecord{..} where rThree = read $ unpack rThreeTxt -- 可选兜底分支,遇到不符合预期的输入时抛出明确错误,方便排查问题 readText l = error $ "预期长度为3的输入列表,实际长度为" ++ show (length l)
方案2:适配生产环境的容错实现
如果后续需要处理脏数据等异常输入,可以配合readMaybe返回Maybe MyRecord避免运行时崩溃:
{-# LANGUAGE RecordWildCards #-} import Data.Text (Text, unpack) import Text.Read (readMaybe) import Data.Maybe (mapMaybe) readText :: [Text] -> Maybe MyRecord readText [rOne, rTwo, rThreeTxt] = do rThree <- readMaybe $ unpack rThreeTxt pure MyRecord{..} readText _ = Nothing
结合你补充的CSV解析需求,整体读取文件的逻辑可以写成:
import qualified Data.Text as T import qualified Data.Text.IO as T -- 返回所有行的解析结果,解析失败的为Nothing readMyRecordCsv :: FilePath -> IO [Maybe MyRecord] readMyRecordCsv path = do content <- T.readFile path let splitLines = T.splitOn "," <$> T.lines content pure $ readText <$> splitLines -- 如果要直接过滤掉解析失败的行,使用这个版本 readMyRecordCsvSkipInvalid :: FilePath -> IO [MyRecord] readMyRecordCsvSkipInvalid path = do content <- T.readFile path let splitLines = T.splitOn "," <$> T.lines content pure $ mapMaybe readText splitLines
内容的提问来源于stack exchange,提问作者Jivan
相关产品推荐
相关产品推荐

