【问题标题】:Notation for a recursive call on user defined type [duplicate]用户定义类型的递归调用的表示法[重复]
【发布时间】:2012-10-19 12:38:56
【问题描述】:

可能重复:
Iterating through a String and replacing single chars with substrings in haskell

我正在尝试实现一个查看字符串 ([Chars]) 并检查每个字母是否应该用另一个字符串替换的函数。例如,我们可能有一个由“XYF”和“X = HYHY”、“Y = OO”的规则组成的 [Chars],那么我们的输出应该变成“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  = 

【问题讨论】:

    标签: haskell types recursion notation


    【解决方案1】:

    我建议使用辅助函数来检查规则是否匹配,

    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
    

    如果您尝试在apply 内对rules 进行显式递归,它会变得太丑陋,因为您需要记住完整的规则列表以替换后面的字符。

    【讨论】:

    • 我认为不需要辅助函数,因为您可以检查 x 是否等于 t (在我的示例中),但我知道您的方法在哪里。感谢您的意见。
    • 问题是你有两个列表要遍历,字母列表和规则。在同一个函数中这样做并不好,因为您需要恢复下一个字母的完整规则集,您需要三个参数。将两个遍历分开可以使代码更短、更易于理解和维护。
    【解决方案2】:

    我建议您学习使用通用实用程序函数来做到这一点。您想要的两个关键功能:

    1. lookup :: Eq a => a -> [(a, b)] -> Maybe b。在关联列表(用于表示地图或字典的对的列表)中查找映射。
    2. 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 的参数顺序,因为当参数与结果具有相同类型时,通常有助于使该参数成为最后一个参数(更容易链接函数)。

    我们可以更进一步,通过使用来自Data.Maybe 模块(fromMaybe default Nothing = default, fromMaybe default (Just x) = x)的实用函数fromMaybe :: a -> Maybe a -> a 将其变成单行:

    import Data.Maybe
    
    apply rules = concatMap (\c -> fromMaybe [c] $ lookup c rules)
    

    您可以做的一个练习是自己编写所有这些实用函数的版本:lookupconcatMap(将其分解为concat :: [[a]] -> [a]map :: (a -> b) -> [a] -> [b]),以及fromMaybe。这样你就可以理解这个解决方案所涉及的“全栈”。

    【讨论】:

      【解决方案3】:

      我的解决方案在结构上与其他解决方案相似,但使用了 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中使用列表推导。

      【讨论】:

        猜你喜欢
        • 2017-07-26
        • 1970-01-01
        • 1970-01-01
        • 2014-01-27
        • 1970-01-01
        • 1970-01-01
        • 2019-05-19
        • 1970-01-01
        相关资源
        最近更新 更多