2011-12-09 29 views
2

我目前正在使用CSV文件將其解析爲[[String]] 該數組中的第一個[String]是頭文件,例如:Haskell計數列表滿足查詢的元素

["Code","Address","Town"] 

,其餘是信息

["ABA","12,east road", "London"] 

陣列我想創建一個查詢系統中輸入,結果會是這個樣子

>count "Town"="*London*" @1="A*" 
14 rows 

列名可以作爲一個字符串或作爲@與索引的列 我有一個case switch來識別第一個字輸入,因爲我要展開我的CSV閱讀器的不同功能。 當它看到字數時,它將轉到一個函數,該函數將返回一個行數。我不知道如何開始做解析的查詢。 起初,我想我可能會將字數統計後的結果字符串拆分爲每個查詢的字符串列表,執行一個並使用滿足此查詢的列表再次檢查下一個查詢,並留下所有查詢的列表滿足,然後計算條目數量並返回它們。將會有一個case switch來識別第一個輸入是字符串還是@符號。 *用於表示單詞後面的零或任何字符。 我不知道如何開始實施這個,或者如果我錯過了我可能遇到的問題,我的解決方案。我會很樂意爲你提供任何幫助。我不是很高級的Haskell(因爲我剛剛開始),所以我也希望保持簡單。謝謝

+0

你想在haskell中創建一個'微語言'嗎?或者你想分析整個字符串?也就是說,'>'是你的應用程序的ghci提示符或提示符? – ondra

+0

我把>來區分控制檯輸入和輸出:)我有一個主要的程序,我運行,這就像一個命令窗口,我可以加載一個CSV文件解析它保存等。我想創建一種用於練習的CSV文件上的SQL查詢系統。 – DustBunny

回答

9

以下是一種可能的方法。

首先,讓我們從列表中一覽的字符串您表示移開一下,讓我們代表記錄鍵/值對,使得數據庫僅僅是一個記錄列表:

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 = ... 

,但我建議使用解析器組合子庫,例如parsecuulib。)

現在,讓我們測試一下。首先,我們介紹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 

當然,這只是一種方法,當然可以考慮很多方法。正如我們以前所做的那樣,在小函數中解決問題的一個好方面是,您可以輕鬆地單獨測試和試驗小塊解決方案。

+0

非常感謝您爲您的答案付出的努力,非常感謝。我得到了一個解析CSV文件的巨大項目,我覺得我沒有資格自己完成這項工作,因爲我只在不到3個月的時間裏學習了作爲大學模塊一部分的haskell。而對於這個項目,我不能從互聯網以外獲得任何外部幫助。我一整天都在看你的代碼,即使如此,我也不明白一些事情。但是,謝謝 – DustBunny

1

我不是在Haskell那麼多精通要麼...但我想接近它是這樣的:你想要什麼本質上是:

f $ filter g list 

其中「F」可以像「計數」 (這實際上是長度),'g'是與您的查詢相對應的過濾功能。首先,你將輸入分爲'head'和'tail'(這就是列表);那麼你可以使用Parsec來解析查詢。你的parsec解析器只會返回一個元組;首先是一個函數'f'(如果遇到'count',那可能是'length');第二個會簡單地返回true/false;你將有這幾種:

f :: [String] -> Int 
g :: [String] -> Bool 

構建「F」和「G」是parsec很容易的。我想如果你對鏈接頁面上的例子稍微玩一下,你會發現自己。

相關問題