首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用Haskell读取CSV文件

使用Haskell读取CSV文件
EN

Code Review用户
提问于 2021-09-16 01:02:50
回答 1查看 1.1K关注 0票数 5

我在用Haskell看CSV。我不确定这样做是否合适。

这就是我要做的:

  1. 从CSV ->读取行返回延迟字节字符串
  2. 将CSV中的标头和行解析为元组-> ( headers,)
  3. 删除标头->
  4. 过滤“普通股”->的股票
  5. 打印产生的股票

任何关于如何编写更好的Haskell代码的反馈都是非常感谢的!

代码是一个堆栈项目,您可以找到关于如何运行它的项目和说明:这里

在编写代码:我希望我在学习Haskell的时候知道之前,我阅读了Stephen指南的这一部分

下面是读取CSV文件的代码。主要功能是printStocks

代码语言:javascript
运行
复制
{-# LANGUAGE OverloadedStrings #-}

module Lib (printStocks) where

import Control.Monad
import qualified Data.ByteString.Lazy as BL
import Data.Csv
import qualified Data.Vector as V

-- data type to model a stock
data Stock = Stock
  { code :: String,
    name :: String,
    country :: String,
    exchange :: String,
    currency :: String,
    instrumentType :: String
  }
  deriving (Show)

instance FromNamedRecord Stock where
  parseNamedRecord record =
    Stock
      <$> record .: "Code"
      <*> record .: "Name"
      <*> record .: "Country"
      <*> record .: "Exchange"
      <*> record .: "Currency"
      <*> record .: "Type"

-- type synonyms to handle the CSV contents
type ErrorMsg = String

type CsvData = (Header, V.Vector Stock)

-- Function to read the CSV
parseCSV :: FilePath -> IO (Either ErrorMsg CsvData)
parseCSV filePath = do
  contents <- BL.readFile filePath
  return $ decodeByName contents

-- Discard headers from CsvData
removeHeaders :: CsvData -> V.Vector Stock
removeHeaders = snd

-- Check if the given element is a Common Stock
isStock :: Stock -> Bool
isStock stock = instrumentType stock == "Common Stock"

filterStocks :: V.Vector Stock -> V.Vector Stock
filterStocks = V.filter isStock

-- Print the stocks from the CSV file
printStocks :: FilePath -> IO ()
printStocks filePath =
  parseCSV filePath
    >>= print . fmap (filterStocks . removeHeaders)
EN

回答 1

Code Review用户

回答已采纳

发布于 2021-09-20 13:20:00

总体上看起来很棒,尤其是如何包含运行它的说明,即使是使用示例数据,这也使得这是一个很好的提交。

我注意到的第一件事是打开文件的错误处理。例如,parseCsv检查文件的存在--这是一个很大的提示,表明该函数并不像预期的那样在所有情况下都能正常工作,比如如果文件存在,但是无法读取(尝试chmod a-r test-resources/empty-file.csv并查看它如何产生一个未显示的异常*** Exception: test-resources/empty-file.csv: openBinaryFile: permission denied (Permission denied))。根据签名,我实际上希望通过Either来处理这个问题:

代码语言:javascript
运行
复制
parseCsv filePath = do
  result <- try $ BL.readFile filePath
  return $ case result of
             Left (exception :: IOException) -> Left $ show exception
             Right contents -> decodeByName contents

我相信这会做得更好,这一次也需要启用ScopedTypeVariables

IMO Csv看起来很奇怪,但是这个包已经使用了这个名字,我想这很好。

有些评论可能会更好,比如parseCsv说“函数读取CSV”--嗯,是的,我们已经从名字中看到了这一点。可能“从文件中读取原始CSV数据”,类似于readStocks

我可能会在filterStocks中内联本地函数,因为我认为它没有为匿名函数增加太多的清晰度:

代码语言:javascript
运行
复制
filterStocks = V.filter (\instrument -> instrumentType instrument == "Common Stock")

由于您已经自由地使用了fmap,所以我还考虑了以下几点:

代码语言:javascript
运行
复制
filterStocks = V.filter $ fmap (== "Common Stock") instrumentType

然而,我认为它实际上使它在可读性方面更糟。

类似地,readStocks及其fmap . fmap太复杂了,我跟不上。我认为,对下一位读者来说,稍微扩展一下它可能会更好。

票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/268038

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档