Haskell 在重用多值构造函数模式匹配时避免重复代码
假设您有一个具有多个值构造函数的数据结构,例如Haskell 在重用多值构造函数模式匹配时避免重复代码,haskell,Haskell,假设您有一个具有多个值构造函数的数据结构,例如LogMessage数据结构,如下所示: data LogMessage = Unknown String | LogMessage MessageType TimeStamp String 如果消息可以正确解析,它会有一些额外的数据,然后是一个字符串。如果它不能被解析,那么它只是一个包罗万象的未知字符串 或者假设您正在处理类似于的字符串或字符串,因此您可能正在处理左字符串或右字符串 现在让我们假设您希望对基础数据
LogMessage
数据结构,如下所示:
data LogMessage = Unknown String
| LogMessage MessageType TimeStamp String
如果消息可以正确解析,它会有一些额外的数据,然后是一个字符串
。如果它不能被解析,那么它只是一个包罗万象的未知字符串
或者假设您正在处理类似于的字符串或字符串
,因此您可能正在处理左字符串
或右字符串
现在让我们假设您希望对基础数据应用相同的处理步骤,而不管它位于哪个值构造函数中
例如,我可能希望检测LogMessage
字符串中的某个单词,因此我可以使用如下函数:
detectWord :: String -> LogMessage -> Bool
detectWord s (Unknown m) = isInfixOf s (map toLower m)
detectWord s (LogMessage _ _ m) = isInfixOf s (map toLower m)
或者,它可以很容易地编写为将字符串
作为输入来处理,而不是日志消息
在这两种情况下,我必须重复完全相同的代码(isInfixOf…
部分),因为由于在不同的值构造函数上进行模式匹配,我必须提取它将以不同方式操作的底层数据
对于每个不同的值构造函数匹配,必须重复/复制粘贴代码,这是很糟糕的
在没有复制/粘贴代码的情况下,如何编写此类Haskell函数?如何只编写一次底层逻辑,然后解释如何在许多不同的值构造函数模式中使用它
简单地将其移动到辅助函数将减少字符数,但并不能真正解决问题。例如,与第一种情况相比,以下关于“不要重复你自己”的想法在本质上并不好:
helper :: String -> String -> Bool
helper s m = isInfixOf s (map toLower m)
detectWord :: String -> LogMessage -> Bool
detectWord s (Unknown m) = helper s m
detectWord s (LogMessage _ _ m) = helper s m
同样,我们必须对每一种不同的模式说同样的话。简单,不会让人讨厌你
尝试使用
复杂,可能会让人讨厌你
使用和
编写一个函数,在两种情况下都获取消息。然后,您就不需要为不关心的用途编写单独的案例:
getMsg (Unknown m) = m
getMsg (LogMessage _ _ m) = m
detectWord s log = infixOf s (map toLower (getMsg log))
请注意,有些东西需要检查您的类型的大小写,而
getMsg
的大小写与这些内容一样少。我可能有误解,但您似乎也可以使用您提供的帮助函数。然后它可能是detectWord-pineel lm=isInfixOf-pineel(stringoflgmessage lm)
。视图模式很好,我只是想大声说出来,它看起来没有必要。看来,我的困惑是,我没有把写助手函数的想法当作一种抽象模式匹配转换为字符串的方法——我只想到使用“右手边”逻辑的帮助函数。也许我对它们的语法还不够习惯,但我似乎永远记不清哪一部分去了哪里。我的想法是:foo x y实际上是foo(id->x)(id->y)。我认为在设计抽象数据类型时,几乎可以肯定的是,在用户指南打开的情况下编写适当的模式同义词(一次!),所以用户不必考虑特殊的语法。
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE NoImplicitPrelude #-}
module Lib where
import BasePrelude
import Control.Lens
import Generics.Deriving.Lens
data LogMessage = Unknown String
| LogMessage () () String
deriving (Generic)
detectWord :: String -> LogMessage -> Bool
detectWord needle =
allOf tinplate (isInfixOf needle . map toLower)
getMsg (Unknown m) = m
getMsg (LogMessage _ _ m) = m
detectWord s log = infixOf s (map toLower (getMsg log))