2016-11-06 90 views
2

我試圖從列表中刪除具有給定謂詞的塊。我希望使用雙字符,例如~/,但已解決只使用$。我基本上是想要做的就是這個...從Haskell字符串切割特定塊

A: "Hello, my $name is$ Danny and I $like$ Haskell"

我想要把它變成是這樣的:

B: "Hello, my Danny and I Haskell"

所以我想在給定符號間剝離一切,$,或者我的第一選擇是~/,如果我能算出來的話。我試過的是這樣的:

s1 :: String -> String 
s1 xs = takeWhile (/= '$') xs 

s2 :: String -> String 
s2 xs = dropWhile (/= '$') xs 

s3 :: String -> String 
s3 xs = s3 $ s2 $ s1 xs 

這個解決方案似乎只是bug我的IDE(可能無限循環)。

解決方案:

s3 :: String -> String 
s3 xs 
|'$' `notElem` xs = xs 
|otherwise = takeWhile (/= '$') xs ++ (s3 $ s1 xs) 

s1 :: String -> String 
s1 xs = drop 1 $ dropWhile (/= '$') $ tail $ snd $ break ('$'==) xs 
+1

你的例子'A'實際上生成'Hello,.my..Danny.and.I..Haskell'。我使用點而不是空格,因爲不知何故,SO會在註釋中刪除多餘的空白(即使在代碼塊中)。 – ThreeFx

+0

幹得好!請注意'drop 1'只是'tail'。另外,你可以稍微清理一下代碼,但使用模式匹配和'where'子句。正如@ChrisMartin所說,「Parser」也可以使用,特別是對於更復雜的問題。 – ThreeFx

回答

2

你的無限循環中來自呼叫s3遞歸沒有基本情況:

s3 :: String -> String 
s3 xs = s3 $ s2 $ s1 xs 

加入鹼的情況下修正無限循環:

s3 xs 
    | '$' `notElem` xs = xs 
    | otherwise = ... 

這並不是完整的答案。想想s1實際上做何使用它的返回值:

s1 "hello $my name is$ ThreeFx" == "hello " 

有關進一步的參考,請參閱break功能:

break :: (a -> Bool) -> [a] -> ([a], [a]) 
+0

謝謝你回答(並編輯我的帖子,試圖獲得這種格式的年齡)。 因此,s1的輸出應該添加到新列表(我的猜測),還是在s2運行之後剩下的內容的末尾?我已經看了你的s1的例子,顯然和我一樣,它會刪除整個字符串的剩餘部分,並且只是在$之前獲取內容,那麼我將如何修改以獲取$傳遞給s2之後的所有內容? –

+0

@DannyWilson不客氣!要將代碼格式化爲代碼,只能使用四個空格,* not *以'>'開頭。 – ThreeFx

+0

我跟Haskell學習的主要問題是學習語法,所以有了警惕,第一個案例在解析完成時會通過(我在猜測),否則將調用takeWhile/dropWhile函數? –

4

這似乎是解析器一個很好的應用。使用trifecta A液:

import Control.Applicative 
import Data.Foldable 
import Data.Functor 
import Text.Trifecta 

input :: String 
input = "Hello, my $name is$ Danny and I $like$ Haskell" 

cutChunk :: CharParsing f => f String 
cutChunk = "" <$ (char '$' *> many (notChar '$') <* char '$') 

cutChunk比賽$,隨後0以上(many)非$字符,然後另一個$。然後我們使用("" <$)使這個解析器的值始終爲空字符串,從而丟棄此解析器匹配的所有字符。


includeChunk :: CharParsing f => f String 
includeChunk = some (notChar '$') 

includeChunk我們想在結果,這是什麼,這不是$字符包括文本相匹配。我們使用some(匹配一個或多個字符)而不是many(匹配零個或多個字符)是很重要的,因爲我們接下來要將該解析器包含在另一個many表達式中;如果這個解析器與空字符串匹配,那麼它可以無限循環。


chunks :: CharParsing f => f String 
chunks = fold <$> many (cutChunk <|> includeChunk) 

chunks是一切解析器。閱讀<|>爲「或」,如「解析cutChunkincludeChunk」。 many (cutChunk <|> includeChunk)是一個解析器,它產生一個塊的列表,例如Success ["Hello, my ",""," Danny and I ",""," Haskell"],所以我們fold輸出將這些塊連接成一個單一的字符串。


result :: Result String 
result = parseString chunks mempty input 

結果:

Success "Hello, my Danny and I Haskell" 
+0

謝謝您的解決方案!我正在使用的實際庫是解析器,我真的想從底層開始實現,稍後會引入更高級的解析(如您的示例)。儘管謝謝你的解決方案!是一個有趣的閱讀,並且無疑會在我的項目中幫助我! –

+0

這是一個優雅的方法。也許增加一些評論也可以讓更多的人從未使用trifecta。它大部分是自我記錄,以訓練有素的眼睛,但我不得不暫停猜測,例如,爲什麼'「」<$'和爲什麼'摺疊<$>'。 – chi

+0

有些意見會有幫助! (這將有助於我來更高級的解析) –

2

我覺得你的邏輯是錯誤的,也許更容易把它寫在一個基本方式

Prelude> let pr xs = go xs True 
Prelude|   where go [] _ = [] 
Prelude|     go (x:xs) f | x=='$' = go xs (not f) 
Prelude|        | f = x : go xs f 
Prelude|        | otherwise = go xs f 
Prelude| 

Prelude> pr "Hello, my $name is$ Danny and I $like$ Haskell" 
"Hello, my Danny and I Haskell" 

說明標誌f保持追蹤狀態(無論是否通過模式)。如果當前字符是一個令牌跳過和切換狀態。

+0

這很有趣,肯定比我的方法更簡單。我不認爲你可以再解釋一遍,以便將來可以使用類似的邏輯?特別是去和單獨的冒號部分? –

+0

謝謝你的回覆!我從來沒有意識到這樣一個簡單的問題最終會變成這樣的學習曲線,我有很多想法!我認爲Haskell也可能成爲我最喜歡的語言! –