Function Haskell中的组合函数

Function Haskell中的组合函数,function,haskell,functional-programming,Function,Haskell,Functional Programming,如何在Haskell中组合这些类似的函数 getGroup [] acc = reverse acc getGroup ((Letter x):letfs) acc = getGroup letfs ((Letter x):acc) getGroup ((Group x):letfs) acc = getGroup letfs ((Group (makeList x)):acc) getGroup ((Star x):letfs) acc = getGroup letfs ((Star (mak

如何在Haskell中组合这些类似的函数

getGroup [] acc = reverse acc
getGroup ((Letter x):letfs) acc = getGroup letfs ((Letter x):acc)
getGroup ((Group x):letfs) acc = getGroup letfs ((Group (makeList x)):acc)
getGroup ((Star x):letfs) acc = getGroup letfs ((Star (makeList x)):acc)
getGroup ((Plus x):letfs) acc = getGroup letfs ((Plus (makeList x)):acc)
getGroup ((Ques x):letfs) acc = getGroup letfs ((Ques (makeList x)):acc)
字母、组、星形、加号和QUE都是数据类型定义的一部分

data MyData a 
        = Operand a 
        | Letter a
        | Star [RegEx a] 
        | Plus [RegEx a] 
        | Ques [RegEx a]
        | Pipe [RegEx a] [RegEx a]
        | Group [RegEx a]
        deriving Show
我想知道是否有更好的方法来编写这些函数,因为它们具有相似性。
大多数情况下,我希望组合Group、Star、Plus和Ques的函数,因为它们是相同的,但如果有一种方法可以组合所有这些函数,那就更好了。

如果不使用模板Haskell,就无法摆脱模式匹配的重复,这可能不值得只使用五个不同的构造函数。不过,您可以消除许多其他重复,并改进函数的性能特征

getGroup = map go
  where go (Letter x) = Letter x
        go (Group x) = Group . makeList $ x
        go (Star x) = Star . makeList $ x
        go (Plus x) = Plus . makeList $ x
        go (Ques x) = Ques . makeList $ x

除了更加简洁之外,它还消除了尾部递归,这种递归在Haskell这样的惰性语言中会导致空间泄漏。

当您将数据类型定义为多个不同情况的不相交并集时,在处理这种类型的函数中,您将不可避免地遇到大量的案例分析

减少案例分析的一种方法是通过分解公共性来简化基本类型:

data MyData a = Val String a 
              | UnOp String [Regex a]
              | BinOp String [Regex a] [Regex a]
在这个公式中,每个案例都有一个鉴别器字段,您可以用它来区分每个案例的不同类型。在这里,我只是使用字符串,假设您给它们命名,如“操作数”、“字母”、“星”等,但您也可以为
Val
、UnOp等类型的有效鉴别器定义单独的枚举类型

在这种情况下,你失去的主要是类型安全;您可以使用我提供的
字符串
字段来构造特别无意义的东西。解决这个问题的第一个方法是使用所谓的智能构造函数;这些函数具有特定类型的参数,可以以类型安全的方式构建更弱类型的核心数据。只要您不从模块中导出实际的
MyData
构造函数,您类型的其他用户将只能通过您的智能构造函数构造合理的数据


如果您想从类型构造函数本身获得更多安全构造的保证,那么您应该转向广义代数数据类型(GADT)和幻象类型的概念。其基本思想是在数据类型定义的
=
左侧的类型变量和右侧的类型变量之间建立更灵活的关系。不过,它们是Haskell的一个新的高级功能,因此您可能希望在牢牢掌握标准Haskell数据类型之前不要使用它们。

对不起。。。我是哈斯克尔的新手。因此,我的函数字母(例如)实际上将变成:
go((字母x):lefts)acc=getGroup lefts((字母x):acc)
?不,去掉累加器。尾部递归在处理惰性求值时不好;它会导致空间泄漏。最好使用像
map
这样的高阶函数,但是如果要使用显式递归,它应该是这样的
getGroup((字母x):lefts)=字母x:getGroup lefts
。我得到了这个错误:
解析输入错误
go'
,这是在第二次执行时发生的:
getGroup=map go where go(字母x):lefts)=字母x:getGroup lefts go((星x):lefts)=(星x):getGroup lefts go((加x):lefts)=(加x):getGroup lefts go((加x):getGroup lefts go((问题x):lefts)=(问题x):getGroup lefts go((问题x):getGroup lefts go((问题x):getGroup lefts)=(问题x):getGroup lefts `(所以我认为第一个是可以的)。此外,使用显式递归似乎可以工作:D,但它仍然分为五个函数。尝试从回复中逐字复制答案并手动复制,应该可以工作。您的
go
定义与答案中的定义不同。