2012-10-26 29 views
5

學習使用Parsec庫,作業的一部分。Haskell Parsec跳過沒有預定義的所有單詞

編輯:歡迎使用其他庫的建議,重點是解析。

我想要的是從任何句子中提取大寫字母和四個指南針方向的所有單詞。例如:「比利時完全位於荷蘭南部。」應該找到並返回「比利時南荷蘭」。

我無法理解的是如何忽略(吃)任何不是指南針方向的輸入。 我希望找到沿

'many (not compassDirection >> space)' 

但克(1H)oogle是沒有幫助我行的東西。

下面的代碼明顯停留在「多」功能上。

readExpr :: String -> String 
readExpr input = case parse (parseLine) "" input of 
    Left err -> "No match: " ++ show err 
    Right val -> "Found: " ++ showVal val 

parseLine :: Parser GraphValue 
parseLine = do 
      x <- parseCountry 
      space 
      many (some (noneOf " ") >> space) 
      y <- parseCompass 
      space 
      many (some (noneOf " ") >> space) 
      z <- parseCountry 
      return $ Direction [x,y,z] 

compassDirection :: Parser String 
compassDirection = string "north" <|> 
        string "south" <|> 
        string "east" <|> 
        string "west" 

parseCountry :: Parser GraphValue 
parseCountry = do 
       c <- upper 
       x <- many (lower) 
       return $ Country (c:x) 

parseCompass :: Parser GraphValue 
parseCompass = do 
       x <- compassDirection 
       return $ Compass x 
+1

(只是風格上,你可以寫'compassDirection =選擇$地圖字符串[「北」,「南」,「東」,「西」]'。) – huon

+0

做得好誠實,清晰,解決目前爲止的問題並提供您現有的代碼。一個很好的問題。 +1 – AndrewC

回答

4

我不會詳談,因爲這是家庭作業,OP說「重要的是解析」。


我會解決這個問題的方法:

  • 令牌化的輸入。分解成單詞;這將釋放真正的解析步驟而不必擔心令牌定義(即「是%#@ [一部分的詞?」)還是空白。這可能與words一樣簡單,或者您可以使用Parsec進行標記。那麼你將有[Token](或[String]如果你願意)。

  • 指南針方向的解析器。你已經有了這個(很好的工作),但是如果輸入是[String]而不是String,它將不得不修改一下。

  • 對於以大寫字母開頭的單詞的解析器。

  • 其他所有解析器,只要它看到一個不是指南針方向的標記或一個以大寫字母開頭的單詞就會成功。

  • 解析器適用於任何標記,但區分好的東西和不好的東西,可能使用代數數據類型。

  • 上很多記號的工作

希望這顯然沒有被解析器太清晰;例如,您仍然需要擔心何時丟棄垃圾。其基本思想是將問題分解成許多小問題,解決子問題,然後將這些解決方案粘合在一起。

0

難道你不能把字符串拆分成words,filter那些以大寫字母開頭或是指南針方向的字符串,然後unwords他們回來嗎?無需拔出Parsec槍。

+1

這只是我得到的基礎知識的竅門。最終,我們應該儘可能地分析自然語言。 「德國和意大利不共享邊界​​,但比利時和瑞典都這樣做。」 我想我最好的選擇是開始弄清楚如何解析真正的基本預定義句子。 – Taelia

3

我打算告訴你我將如何開始,然後就如何繼續進行建議。

我的依據是一個抽象的數據結構 - 當您添加多餘的話,你可以更緊密地將其分類:

data Word = Country String | Direction NSEW | Unclassified String 
data NESW = North | East | South | West 

,所以你不知道我的回答對你如何跳過的話大約是您不需要 - 將它們保留爲未分類。

應用風格比monadic風格更好。

我覺得compassDirection應該讓資本:

compassDirection :: Parser NESW 
compassDirection = north <|> south <|> east <|> west where 
    north = North <$ (string "north" <|> string "North") 
    east = ... 

可以使用Country <$> ((:) <$> upper <*> many lower)

然後你就可以擁有一個包羅萬象的Unclassified <$> many letter定義country

你的話解析器目前可以

word = compassDirection <|> country <|> unclassified 

但是請注意,compassDirection有來country之前,否則country將匹配North

你可以在這行的時刻做

words = word `sepBy1` space 

,但是你必須要一定不正確使用wordwords當你分析句子,因爲你失去了這個詞,是什麼樣的控制。在這一點上,你需要noun,adjective,nounPhrase,verb,adjective,adjectivalPhrase等等來詮釋句子結構。你無法解析的句子意味着你需要在語法中添加新的構造。

值得讓詞語解析器吞下它們後面的空白(或之前),或者使用預處理器重構空格和標點符號中的單詞。如果您是英國人,請考慮使用fullStop解析器,如果您是美國人,請考慮使用解析器period。在創建句子分析器時使用它。

使用應用程序和更高階的函數將使你的語法寫得更清晰,因爲你不會把它與一元符號混雜在一起,它會看起來像句子。 示例:如果要針對每個語法對象使用主要具有抽象數據結構(AST)方法和一個構造函數,則可以執行nvn = NVN <$> noun <*> verb <*> noun。 如果你只是喜歡圍繞所有相同類型放置一堆單詞,你可以做nvn = sequence [noun,verb,noun]

大多數計算機語言都是使用AST方法解析的,但是除了我從我妻子的語言學學位獲得的二手資料之外,我沒有直接的自然語言解析經驗。

如果您坐下來寫下如何將單詞,短語,從句和句子的類別組合在一起,您就可以快速編寫解析器。

+0

令人驚歎的答案,謝謝你的努力。我會讓信息沉浸在我身上,然後我會嘗試實現所有這些。 – Taelia