[英]Filtering a table using data constructors
I'm trying to learn Haskell and I have no idea how to continue from here.我正在尝试学习 Haskell,但我不知道如何从这里继续。 I added below the part of code that matters.
我在重要的代码部分下面添加了。 So I defined a
Query
with multiple values, and the last one I got stuck one filters the rows in the table after a specific condition.所以我定义了一个具有多个值的
Query
,最后一个我卡住了一个在特定条件后过滤表中的行。 So for example it filters the rows that only have the column "Final grade" > 5. I use FilterOp
on each row to see if I can keep it or not, and my table only has Strings
and Floats
in it, so I made an instance for each one.因此,例如,它过滤只有“最终成绩”> 5 列的行。我在每一行上使用
FilterOp
来查看是否可以保留它,而我的表中只有Strings
和Floats
,所以我做了一个每个实例。 After finishing the conditions I have to write the evaluation for the data constructor but I'm not sure that part is correct either.完成条件后,我必须为数据构造函数编写评估,但我也不确定那部分是否正确。 The input will be something like this:
Filter (Gt "Final grade" (read "5":: Float)) (FromCSV example_csv)
.输入将是这样的:
Filter (Gt "Final grade" (read "5":: Float)) (FromCSV example_csv)
。
type CSV = String
type Table = [[String]]
data Query = FromCSV CSV
| ToCSV Query
...
| forall a. FEval a => Filter (FilterCondition a) Query
data FilterCondition a =
Eq String a |
Lt String a |
Gt String a
type FilterOp = Row -> Bool
class FEval a where
feval :: [String] -> (FilterCondition a) -> FilterOp
-- This is what I have to do
instance FEval Float where
...
instance FEval String where
...
-- I'm not sure this is right
instance Filter (FilterCondition a) Query where
...
It's hard to give you specific advice but the problem was fun so I decided to show you what I consider close to your problem:很难给你具体的建议,但这个问题很有趣,所以我决定向你展示我认为接近你的问题的内容:
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE GADTs #-}
module Main where
import Data.List (intercalate)
main :: IO ()
main = putStrLn $ query exampleQuery
----------------------------------------------------------------------
-- Example
exampleCSV :: CSV
exampleCSV = "Name,Grade\nBob,5\nJill,2\nSandy,1"
-- > query exampleQuery
-- "Name,Grade\nJill,2\nSandy,1\n"
exampleQuery :: Query CSV
exampleQuery = ToCSV $ Filter (Lt "Grade" (5 :: Int)) $ FromCSV exampleCSV
----------------------------------------------------------------------
-- Types
-- | Query - a = result-type
data Query a where
-- | query to turn a CSV into a table
FromCSV :: CSV -> Query Table
-- | query to turn a table into a CSV
ToCSV :: Query Table -> Query CSV
-- | query to filter a table given a filter-condition
Filter :: forall a. TableFilter a => FilterCondition a -> Query Table -> Query Table
type CSV = String
-- | Table holds rows (= list of values) and a list of column-names
data Table = Table [Row] [Column]
type Column = String
type Row = [String]
class TableFilter a where
filterRow :: FilterCondition a -> [Column] -> Row -> Bool
data FilterCondition a
= Eq Column a
| Lt Column a
| Gt Column a
----------------------------------------------------------------------
-- Query
query :: Query a -> a
query (FromCSV csv) = fromCSV csv
query (ToCSV tableQuery) = toCSV (query tableQuery)
query (Filter cond tableQuery) = applyFilter cond (query tableQuery)
applyFilter :: TableFilter a => FilterCondition a -> Table -> Table
applyFilter cond (Table rows cols) = Table rows' cols
where rows' = filter (filterRow cond cols) rows
fromCSV :: CSV -> Table
fromCSV = go . lines
where
go [] = error "cannot parse empty CSV - at least a header-line is needed"
go [cols] = Table [] (splitOn ',' cols)
go (cols:tbl) = Table rs cs
where
rs = map (splitOn ',') tbl
cs = splitOn ',' cols
toCSV :: Table -> CSV
toCSV (Table rs cs) =
unlines $ intercalate "," cs : map (intercalate ",") rs
----------------------------------------------------------------------
-- TableFilter instances
instance TableFilter Int where
filterRow = genFilterRow
instance TableFilter Float where
filterRow = genFilterRow
genFilterRow :: (Ord a, Read a) => FilterCondition a -> [Column] -> Row -> Bool
genFilterRow cond cols row =
and $ zipWith (checkCond cond) cols row
where
checkCond (Eq condCol condVal) col val = condCol /= col || condVal == read val
checkCond (Lt condCol condVal) col val = condCol /= col || condVal > read val
checkCond (Gt condCol condVal) col val = condCol /= col || condVal < read val
----------------------------------------------------------------------
-- helpers
splitOn :: Eq a => a -> [a] -> [[a]]
splitOn c = go c []
where
go _ acc [] = [reverse acc]
go c acc (y:ys)
| c == y = reverse acc : go c [] ys
| otherwise = go c (y:acc) ys
the real problem itself is probably encoded in genFilterRow
- I had to do some guessing but I think the [String]
argument in your description is actually the column-names .真正的问题本身可能编码在
genFilterRow
- 我不得不做一些猜测,但我认为您描述中的[String]
参数实际上是column-names 。 So this is just zipping them with the row-values and then using the FilterCondition
together with read
to filter on the matching column-name.所以这只是用行值压缩它们,然后使用
FilterCondition
和read
来过滤匹配的列名。
I did not know what output you expected so I used a GADT to get different outputs for different Query
s (and made it impossible to do something like Filter (Lt "Grade" (5:: Int)) $ ToCSV $ FromCSV exampleCSV
).我不知道您期望什么 output 所以我使用GADT为不同的
Query
获取不同的输出(并且无法执行类似Filter (Lt "Grade" (5:: Int)) $ ToCSV $ FromCSV exampleCSV
)。
The actual filtering can be made rather general ( genFilterRow
= gen
meaning generic ) - did not want to use undecidable instances so I think using this helper and the simple instance FilterTable
is a good compromise.实际的过滤可以做得相当笼统(
genFilterRow
= gen
意思是通用的)——不想使用不可判定的实例,所以我认为使用这个助手和简单的instance FilterTable
是一个很好的折衷方案。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.