从Haskell字符串切割特定块

问题描述:

我试图从列表中删除具有给定谓词的块。我希望使用双字符,例如~/,但已解决只使用$。我基本上是想要做的就是这个...从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

你的无限循环中来自呼叫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函数? –

这似乎是解析器一个很好的应用。使用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

有些意见会有帮助! (这将有助于我来更高级的解析) –

我觉得你的逻辑是错误的,也许更容易把它写在一个基本方式

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也可能成为我最喜欢的语言! –