从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
你的无限循环中来自呼叫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])
谢谢你回答(并编辑我的帖子,试图获得这种格式的年龄)。 因此,s1的输出应该添加到新列表(我的猜测),还是在s2运行之后剩下的内容的末尾?我已经看了你的s1的例子,显然和我一样,它会删除整个字符串的剩余部分,并且只是在$之前获取内容,那么我将如何修改以获取$传递给s2之后的所有内容? –
@DannyWilson不客气!要将代码格式化为代码,只能使用四个空格,* not *以'>'开头。 – ThreeFx
我跟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
是一切解析器。阅读<|>
为“或”,如“解析cutChunk
或includeChunk
”。 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"
谢谢您的解决方案!我正在使用的实际库是解析器,我真的想从底层开始实现,稍后会引入更高级的解析(如您的示例)。尽管谢谢你的解决方案!是一个有趣的阅读,并且无疑会在我的项目中帮助我! –
这是一个优雅的方法。也许增加一些评论也可以让更多的人从未使用trifecta。它大部分是自我记录,以训练有素的眼睛,但我不得不暂停猜测,例如,为什么'“”'。 – chi
有些意见会有帮助! (这将有助于我来更高级的解析) –
我觉得你的逻辑是错误的,也许更容易把它写在一个基本方式
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
保持追踪状态(无论是否通过模式)。如果当前字符是一个令牌跳过和切换状态。
这很有趣,肯定比我的方法更简单。我不认为你可以再解释一遍,以便将来可以使用类似的逻辑?特别是去和单独的冒号部分? –
谢谢你的回复!我从来没有意识到这样一个简单的问题最终会变成这样的学习曲线,我有很多想法!我认为Haskell也可能成为我最喜欢的语言! –
你的例子'A'实际上生成'Hello,.my..Danny.and.I..Haskell'。我使用点而不是空格,因为不知何故,SO会在注释中删除多余的空白(即使在代码块中)。 – ThreeFx
干得好!请注意'drop 1'只是'tail'。另外,你可以稍微清理一下代码,但使用模式匹配和'where'子句。正如@ChrisMartin所说,“Parser”也可以使用,特别是对于更复杂的问题。 – ThreeFx