Haskell计算满足查询的列表的元素
我目前正在处理CSV文件,将其解析为[[String]] 该数组中的第一个[字符串]是头文件,例如:Haskell计算满足查询的列表的元素,haskell,csv,count,Haskell,Csv,Count,我目前正在处理CSV文件,将其解析为[[String]] 该数组中的第一个[字符串]是头文件,例如: ["Code","Address","Town"] 其余的是信息数组 ["ABA","12,east road", "London"] 我想创建一个查询系统,输入和结果如下 >count "Town"="*London*" @1="A*" 14 rows 列名可以作为字符串或带有列索引的@输入 我有一个大小写开关来识别第一个单词输入,因为我要扩展我的CSV阅读器来实现不同的功能。 当
["Code","Address","Town"]
其余的是信息数组
["ABA","12,east road", "London"]
我想创建一个查询系统,输入和结果如下
>count "Town"="*London*" @1="A*"
14 rows
列名可以作为字符串或带有列索引的@输入
我有一个大小写开关来识别第一个单词输入,因为我要扩展我的CSV阅读器来实现不同的功能。
当它看到单词计数时,它将转到一个函数,该函数将返回行数。我不知道如何开始解析查询。
起初,我认为我可能会在单词计数后将结果字符串拆分为一个字符串列表,在每个查询中执行一个,然后使用满足此查询的列表再次检查下一个查询,留下一个满足所有查询的列表,然后计算条目的数量并返回它们。还有一个case开关可以识别第一个输入是字符串还是@符号。
*用于表示零或单词后面的任何字符。
我不知道如何开始实施,或者如果我错过了一个问题,我可能会遇到我的解决方案。如果有人能帮我起步,我会非常乐意。我对Haskell不是很在行(因为我才刚刚开始),所以我也希望保持它的简单。谢谢你我对Haskell也不是很精通……但我会这样做:你想要的基本上是:
f $ filter g list
其中“f”可以是类似“count”的东西(实际上是长度),“g”是与查询对应的过滤函数。首先,您可以将输入分为“head”和“tail”(即列表);然后可以使用Parsec来解析查询。您的parsec解析器将只返回一个元组;第一个是函数“f”(如果遇到“count”,则可能是“length”);第二个简单地返回true/false;您将拥有以下类型:
f :: [String] -> Int
g :: [String] -> Bool
构建“f”和“g”非常容易。我想,如果你在链接页面上稍加研究一下示例,你会自己找到答案。这里有一种可能的方法 首先,让我们稍微远离字符串表示列表,将记录表示为键/值对,这样数据库就是一个记录列表:
type Field = (String, String) -- key, value
type Record = [Field]
type Db = [Record]
在您的表示中读取CSV数据将变成:
type Csv = [[String]]
fromCsv :: Csv -> Db
fromCsv [] = []
fromCsv (ks : vss) = map (zip ks) vss
现在,让我们谈谈查询。在您的设置中,查询本质上是一个筛选器列表,其中每个筛选器标识一个字段并匹配一组值:
type Query = [Filter]
type Filter = (Selector, ValueFilter)
通过名称或基于一的(!)索引选择字段:
通过应用一系列简单解析器来匹配值,其中解析器要么识别单个字符,要么识别零个或多个任意字符的序列:
type ValueFilter = [Parser]
data Parser = Char Char | Wildcard
可以使用成功列表方法实现解析,其中每个成功表示剩余的输入,即解析器未使用的输入部分。剩余输入的空列表表示失败。(因此,请注意以下情况下产生的结果中[[]]
和[[[]]]
之间的差异。)
然后过滤值发展为回溯:
filterValue :: ValueFilter -> String -> Bool
filterValue ps cs = any null (go ps cs)
where
go [] cs = [cs]
go (p : ps) cs = concatMap (go ps) (parse p cs)
值选择非常简单:
select :: Selector -> Record -> Maybe String
select (FieldName s) r = lookup s r
select (FieldIndex n) r | n > 0 && n <= length r = Just (snd (r !! (n - 1)))
| otherwise = Nothing
最后,为了执行一个完整的查询,我们有
exec :: Query -> Db -> [Record]
exec = (flip . foldl . flip) (filter . apply)
(我将查询本身的解析留作练习:
readQuery :: String -> Maybe Query
readQuery = ...
但我建议使用解析器组合器库,如或。)
现在,让我们测试一下。首先,我们介绍一个CSV格式的小型数据库:
csv :: Csv
csv =
[ ["Name" , "City" ]
------- ------------
, ["Will" , "London" ]
, ["John" , "London" ]
, ["Chris", "Manchester"]
, ["Colin", "Liverpool" ]
, ["Nick" , "London" ]
]
然后,我们构造一个简单的查询:
-- "Name"="*i*" @2="London"
query :: Query
query =
[ (FieldName "Name", [Wildcard, Char 'i', Wildcard])
, (FieldIndex 2,
[Char 'L', Char 'o', Char 'n', Char 'd', Char 'o', Char 'n'])
]
事实上,对数据库运行我们的查询会产生:
> exec query (fromCsv csv)
[[("Name","Will"),("City","London")],[("Name","Nick"),("City","London")]]
或者,如果您只是在计算查询结果之后:
> length $ exec query (fromCsv csv)
2
当然,这只是一种方法,可以肯定的是,我们可以想出许多替代方法。如上所述,将问题分解为小函数的一个很好的方面是,您可以轻松地单独测试和实验解决方案的小部分。您想在haskell中创建一种“微语言”吗?还是要分析整个字符串?也就是说,“>”是ghci提示符还是应用程序的提示符?我输入了>以区分控制台输入和输出:)我有一个运行的主程序,这就像一个命令窗口,我可以加载一个csv文件,解析它,保存它,等等。我想在csv文件上创建一个SQL查询系统,以供练习。感谢您在回答中付出的努力,非常感谢。我在CSV文件解析方面有一个巨大的项目要做,我觉得我没有资格自己做这件事,因为我在大学模块中学习haskell不到3个月。对于这个项目,除了互联网,我不能得到任何外界的帮助。我整天都在看你的代码,甚至有些事情我也不懂。但是谢谢你
-- "Name"="*i*" @2="London"
query :: Query
query =
[ (FieldName "Name", [Wildcard, Char 'i', Wildcard])
, (FieldIndex 2,
[Char 'L', Char 'o', Char 'n', Char 'd', Char 'o', Char 'n'])
]
> exec query (fromCsv csv)
[[("Name","Will"),("City","London")],[("Name","Nick"),("City","London")]]
> length $ exec query (fromCsv csv)
2