可能重复:
Iterating through a String and replacing single chars with substrings in haskell
我正在尝试实现一个查看String([Chars])的函数,并检查每个字母是否应该用另一个字符串替换该字母。例如,我们可能有一个由“XYF”组成的[Chars]和表示“X = HYHY”,“Y = OO”的规则,那么我们的输出应该变成“HYHYOOF”。
我想使用我定义的以下两种类型:
type Letters = [Char]
data Rule = Rule Char Letters deriving Show
我的想法是该功能应该使用警卫看起来如下所示。然而问题是,当我想浏览所有规则以查看它们中的任何一个是否适合当前字母x时,我找不到关于如何递归调用的任何信息。我希望任何人都可以给出一些关于符号如何的提示。
apply :: Letters -> [Rule] -> Letters
apply _ _ = []
apply (x:xs) (Rule t r:rs)
| x /= t = apply x (Rule t rs)
| x == t = r++rs:x
| otherwise =
答案 0 :(得分:5)
我建议使用辅助函数来检查规则是否匹配,
matches :: Char -> Rule -> Bool
matches c (Rule x _) = c == x
然后检查每个字符是否有匹配的规则
apply :: Letters -> [Rule] -> Letters
apply [] _ = []
apply s [] = s
apply (c:cs) rules = case filter (matches c) rules of
[] -> c : apply cs rules
(Rule _ rs : _) -> rs ++ apply cs rules
如果您在rules
内的apply
上尝试显式递归,则会变得太丑陋,因为您需要记住完整的规则列表以替换后来的字符。
答案 1 :(得分:2)
我建议您学习使用通用实用程序功能。您需要的两个关键功能:
lookup :: Eq a => a -> [(a, b)] -> Maybe b
。在关联列表中查找映射 - 用于表示地图或字典的对列表。concatMap :: (a -> [b]) -> [a] -> [b]
。这类似于map
,但映射到列表上的函数返回一个列表,结果连接在一起(concatMap = concat . map
)。要使用lookup
,您需要将Rule
类型更改为此更通用的同义词:
type Rule = (Char, String)
还要记住String
是[Char]
的同义词。这意味着concatMap
应用于String
时,会用字符串替换每个字符。现在你的例子可以用这种方式编写(我已经改变了参数顺序):
apply :: [Rule] -> String -> String
apply rules = concatMap (applyChar rules)
-- | Apply the first matching rule to the character.
applyChar :: [Rule] -> Char -> String
applyChar rules c = case lookup c rules of
Nothing -> [c]
Just str -> str
-- EXAMPLE
rules = [ ('X', "HYHY")
, ('Y', "OO") ]
example = apply rules "XYF" -- evaluates to "HYHYOOF"
我改变了apply
的参数顺序,因为当一个参数与结果具有相同的类型时,它通常有助于使该参数成为最后一个(使链接函数更容易)。
我们可以使用fromMaybe :: a -> Maybe a -> a
模块中的效用函数Data.Maybe
(fromMaybe default Nothing
= default
,{{1}进一步将其转换为单线程} = fromMaybe default (Just x)
):
x
您可以做的一项补充就是手动编写所有这些实用程序函数的版本:import Data.Maybe
apply rules = concatMap (\c -> fromMaybe [c] $ lookup c rules)
,lookup
(将其分解为concatMap
并执行此操作concat :: [[a]] -> [a]
)和map :: (a -> b) -> [a] -> [b]
。这样您就可以理解此解决方案中涉及的“完整堆栈”。
答案 2 :(得分:1)
我的解决方案在结构上与其他解决方案类似,但使用monad:
import Control.Monad
import Data.Functor
import Data.Maybe
match :: Char -> Rule -> Maybe Letters
match c (Rule c' cs) = cs <$ guard (c == c')
apply :: Letters -> [Rule] -> Letters
apply cs rules =
[s | c <- cs
, s <- fromMaybe [c] $ msum $ map (match c) rules]
我们正在处理的第一个monad是Maybe a
。它实际上是一个MonadPlus
,它允许我们使用msum
(将[Nothing, Just 2, Nothing, Just 3]
类似于第一个“点击”,这里Just 2
)
第二个monad是[a]
,它允许我们在apply
中使用列表理解。