简体   繁体   English

使用数据构造函数过滤表

[英]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来查看是否可以保留它,而我的表中只有StringsFloats ,所以我做了一个每个实例。 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.所以这只是用行值压缩它们,然后使用FilterConditionread来过滤匹配的列名。


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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM