简体   繁体   English

在Haskell中组合函数

[英]Combining functions in Haskell

How can I combine these similar function in Haskell? 如何在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)

Letter, Group, Star, Plus and Ques are all part of a data type definition. Letter,Group,Star,Plus和Ques都是数据类型定义的一部分。

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

I wonder if there is a better way to write those function because of their similarities. 我想知道是否有更好的方法来编写这些函数,因为它们有相似之处。 Mostly, I wish to combine the functions for Group, Star, Plus and Ques, because they are identical, but if there is a method to combine all of them it would be better. 大多数情况下,我希望将Group,Star,Plus和Ques的功能结合起来,因为它们是相同的,但是如果有一种方法可以将所有这些功能结合起来,那就更好了。

You can't get rid of the repetition of the pattern matching without using Template Haskell, which probably isn't worth it for only five different constructors. 如果不使用模板Haskell,你无法摆脱模式匹配的重复,这对于五个不同的构造函数来说可能是不值得的。 You can eliminate a lot of the other repetition though, and improve the performance characteristics of the function, as well. 您可以消除许多其他重复,并改善功能的性能特征。

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

In addition to being much more concise, it also gets rid of the tail-recursion that will cause a space leak in a lazy language like Haskell. 除了更简洁之外,它还消除了尾部递归,这会导致像Haskell这样的惰性语言中的空间泄漏。

When you have a data type definition defined as you have, as a disjoint union of several different cases, you will inevitably end up with a large mess of case analysis in functions that process that type. 当您拥有一个定义的数据类型定义时,作为几个不同情况的不相交联合,您将不可避免地在处理该类型的函数中结束大量的案例分析。

One approach to reducing the case analysis would be to simplify the base type by factoring out the commonality: 减少案例分析的一种方法是通过分解共性来简化基本类型:

data MyData a = Val String a 
              | UnOp String [Regex a]
              | BinOp String [Regex a] [Regex a]

In this formulation, each case has a discriminator field with which you can tell apart the different kinds of each case. 在这个公式中,每个案例都有一个鉴别器字段,您可以用它区分每种情况的不同类型。 Here, I just used String assuming you'd give them names like "Operand", "Letter", "Star", etc., but you could also define separate enumeration types for the valid discriminators for kinds of Val , kinds of UnOp , etc. 在这里,我只使用了String,假设你给它们命名为“Operand”,“Letter”,“Star”等等,但你也可以为各种Val ,各种UnOp的有效鉴别器定义单独的枚举类型,等等

The main thing you lose in this case is type safety; 在这种情况下你失去的主要是类型安全; you could construct especially nonsensical things with the String fields as I gave them. 你可以用我给它们的String字段构造特别荒谬的东西。 A first approach to tackling this problem is to use what are known as smart constructors ; 解决这个问题的第一种方法是使用所谓的智能构造函数 ; these are functions with specifically-typed parameters that build the more weakly-typed core data in a type-safe manner. 这些是具有特定类型参数的函数,这些参数以类型安全的方式构建更弱类型的核心数据。 As long as you do not export the actual MyData constructors from your module, other users of your type will only be able to construct sensible data via your smart constructors. 只要您不从模块中导出实际的MyData构造函数,您的类型的其他用户将只能通过智能构造函数构建合理的数据。

If you want more guarantees of safe construction from the type constructors themselves, you would want to turn to the concepts of Generalized Algebraic Data Types (GADTs) and Phantom Types . 如果您希望从类型构造函数本身获得更多安全构造的保证,您可能需要转向广义代数数据类型(GADT)幻像类型的概念 The basic idea behind these is to have more flexible relationship between the type variables on the left-hand side of the = of a data type definition and the type variables on the right-hand side. 这些背后的基本思想是在数据类型定义的=左侧的类型变量与右侧的类型变量之间建立更灵活的关系。 They are a somewhat new and advanced feature of Haskell, though, so you may want to hold off on jumping into them until you have a firm grasp on standard Haskell data types. 然而,它们是Haskell的一个新的高级功能,所以你可能想要继续跳进它们,直到你牢牢掌握标准的Haskell数据类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM