以下是一種可能的方法。
首先,讓我們從列表中一覽的字符串您表示移開一下,讓我們代表記錄鍵/值對,使得數據庫僅僅是一個記錄列表:
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
解析可以是:
data Selector = FieldName String | FieldIndex Int
值是通過使用簡單的解析器的一個序列,其中一個解析器或者識別的單個字符或其它的零個或多個任意字符序列匹配使用list-of-successes方法實現,其中每個成功表示剩餘的輸入,即分析器未使用的輸入部分。剩餘輸入的空列表表示失敗。 (因此,請注意,在下面的情況下,產生的結果[]
和[[]]
之間的差異。)然後
parse :: Parser -> String -> [String]
parse (Char c) (c' : cs') | c == c' = [cs']
parse Wildcard [] = [[]]
parse Wildcard [email protected](_ : cs') = cs : parse Wildcard cs'
parse _ _ = []
濾波值發展到回溯:
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
應用記錄過濾器現在相當於在記錄上構造一個謂詞:
apply :: Filter -> Record -> Bool
apply (s, vf) r = case select s r of
Nothing -> False
Just v -> filterValue vf v
最後,執行完整的查詢,我們有
exec :: Query -> Db -> [Record]
exec = (flip . foldl . flip) (filter . apply)
(我離開的查詢分析自己作爲一個練習:
readQuery :: String -> Maybe Query
readQuery = ...
,但我建議使用解析器組合子庫,例如parsec或uulib。)
現在,讓我們測試一下。首先,我們介紹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提示符或提示符? – ondra
我把>來區分控制檯輸入和輸出:)我有一個主要的程序,我運行,這就像一個命令窗口,我可以加載一個CSV文件解析它保存等。我想創建一種用於練習的CSV文件上的SQL查詢系統。 – DustBunny