用户定义类型的递归调用的表示法

时间:2012-10-19 12:38:56

标签: haskell types recursion notation

  

可能重复:
  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  = 

3 个答案:

答案 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)

我建议您学习使用通用实用程序功能。您需要的两个关键功能:

  1. lookup :: Eq a => a -> [(a, b)] -> Maybe b。在关联列表中查找映射 - 用于表示地图或字典的对列表。
  2. concatMap :: (a -> [b]) -> [a] -> [b]。这类似于map,但映射到列表上的函数返回一个列表,结果连接在一起(concatMap = concat . map)。
  3. 要使用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.MaybefromMaybe 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中使用列表理解。