如何修复此代码异味?

时间:2012-01-31 00:02:43

标签: haskell

我有一些代码在进行显式递归,最重要的是它在不需要的时候调用相同的函数两次。我想我可以修复后者,但需要帮助前者。

yesodIdentifier :: Text -> [LDAPEntry] -> Text
yesodIdentifier name ((LDAPEntry _ attributes):_) =
  let (Just eMail) = L.lookup "proxyAddresses" attributes
      partedEmail = L.map (S.splitOn "@") eMail -- now we have   
                                                -- [["garbage:name","domain"]]
  in yesodIdentifier' name partedEmail -- the secondary function is the problem

     where yesodIdentifier' :: Text -> [[String]] -> Text
           yesodIdentifier' nameToMatch ((name:[domain]):rest) =
             if ((unpack nameToMatch) == (sanitized name))
              then (pack (sanitized name)) `append`
                   (pack "@") `append`
                   (pack domain)
              else yesodIdentifier' nameToMatch rest
             where sanitized :: String -> String
                   sanitized dirty =
                     let (garbage:[cleanedName]) = S.splitOn ":" dirty
                     in cleanedName
           yesodIdentifier' name _ = pack "empty identifier"

我想要做的是从LDAPEntry获取(String,[String]),并将其减少到与给定名称匹配的电子邮件地址。

您可以看到递归的位置。如果我能解决这个问题,我就不必担心yesodIdentifier' name _ = pack "empty identifier",因为在用户不在LDAP数据库中的情况下,不会调用此代码。我只是把它放在那里完成。 我认为这可以更好地表达为折叠,但我无法在脑海中正确表达。有什么想法吗?

3 个答案:

答案 0 :(得分:2)

这可以表示为过滤器。这不是一个折叠,因为,如果你进行递归,你会在休息时递归并丢弃之前的条目。您仍然需要处理空列表:

yesodI nameToMatch list = let result = filter (match(unpack nameToMatch)) list
                              match s1 (s2:[domain]) = s1 == s2
                          in case result of 
                             [] -> "empty identifier"
                             res  -> repack (head res)

repack arg@(name:[domain]) = (pack (sanitized name)) `append`
                   (pack "@") `append`
                   (pack domain)

答案 1 :(得分:1)

从ja回答,改写使用find:

yesodI name l = intercalate "@" <$> (find ((name ==) . head) $ filter (not . null) l)

更易阅读的版本

yesodI name list = repack <$> find matchName list'
 where list' = filter (not . null) list
       repack = intercalate "@"
       matchName = (name ==) . head

答案 2 :(得分:0)

您可以先将电子邮件地址清理为(name, domain)对,然后使用find找到匹配名称的第一个电子邮件地址,然后maybe确定如果您执行了找不到匹配:

yesodIdentifier :: Text -> [LDAPEntry] -> Text
yesodIdentifier name ((LDAPEntry _ attributes):_) =
  let (Just eMail) = L.lookup "proxyAddresses" attributes
      name' = T.unpack name
      sanitizedEmail = concatMap sanitize eMail -- now we have [("name","domain")]
      empty = "empty identifier"
      nonEmpty (name, domain) = concat [name, "@", domain]
      sanitize s = do
        (dirtyName:[domain]) <- [S.splitOn "@" s]
        (garbage:[cleanedName]) <- [S.splitOn ":" dirtyName]
        return (cleanedName, domain)
  in T.pack . maybe empty nonEmpty . L.find ((name' ==).fst) $ sanitizedEmail

这是完全未经测试的,因为我没有您提供的所有类型...我希望santize中的List Monad会丢弃无法分解的电子邮件地址{{ 1}}。