在 Haskell 中定义枚举的更好方法

发布于 2024-11-06 20:09:02 字数 356 浏览 3 评论 0原文

我想要一个数据类型来表示可以通过特定名称寻址的有限整数集。我认为最好的方法是使用枚举。

然而,有一个小问题。我知道定义枚举的唯一方法是这样的:

data MyDataType = Foo | Bar | Baz

instance Enum MyDataType 
 toEnum 0 = Foo
 toEnum 1 = Bar
 toEnum 2 = Baz

 fromEnum Foo = 0
 fromEnum Bar = 1
 fromEnum Baz = 2 

请注意,我必须重复同一对两次 - 一次是在定义整数到枚举映射时,另一次是在定义枚举到整数映射时。

有没有办法避免这种重复呢?

I want a datatype to represent a finite set of integers that can be addressed by specific names. I figure the best way to do that is to use an Enum.

However, there is one small problem. The only way I know for defining an Enum is something like this:

data MyDataType = Foo | Bar | Baz

instance Enum MyDataType 
 toEnum 0 = Foo
 toEnum 1 = Bar
 toEnum 2 = Baz

 fromEnum Foo = 0
 fromEnum Bar = 1
 fromEnum Baz = 2 

Note that I have to repeat the same pair two times - one time when defining an integer-to-enum mapping and the other time when defining an enum-to-integer mapping.

Is there a way to avoid this repetition?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

剩一世无双 2024-11-13 20:09:03

已接受解决方案的问题是,当您的表中缺少枚举时,编译器不会告诉您。 派生 Enum 解决方案很棒,但如果您想要任意映射到数字,它就不起作用。另一个答案建议泛型或模板 Haskell。在此基础上使用Data

{-# Language DeriveDataTypeable #-}
import Data.Data
data MyDataType = Foo | Bar | Baz deriving (Eq, Show, Data, Typeable)

toNumber enum = case enum of
   Foo -> 1
   Bar -> 2
   Baz -> 4

添加新构造函数时,我们将在 toNumber 案例映射中收到编译器警告。

现在我们只需要能够将该代码转换为数据,以便可以自动反转映射。在这里,我们生成与接受的解决方案中提到的相同的

table = map (\cData -> let c = (fromConstr cData :: MyDataType) in (c, toNumber c) )
      $ dataTypeConstrs $ dataTypeOf Foo

您可以填写一个 Enum 类,就像在接受的答案中一样。没有提到的是,您还可以填写 Bounded 类。

The problem with the accepted solution is the compiler won't tell you when you are missing an enum in your table. The deriving Enum solution is great, but it won't work if you want to have an arbitrary mapping to numbers. Another answer suggests Generics or Template Haskell. This follows up on that by using Data.

{-# Language DeriveDataTypeable #-}
import Data.Data
data MyDataType = Foo | Bar | Baz deriving (Eq, Show, Data, Typeable)

toNumber enum = case enum of
   Foo -> 1
   Bar -> 2
   Baz -> 4

We will get compiler warning in the toNumber case mapping when a new constructor is added.

Now we just need the ability to turn that code into data so that the mapping can be automatically reversed. Here we generate the same table mentioned in the accepted solution.

table = map (\cData -> let c = (fromConstr cData :: MyDataType) in (c, toNumber c) )
      $ dataTypeConstrs $ dataTypeOf Foo

You can fill out an Enum class just the same as in the accepted answer. Unmentioned there is that you can also fill out the Bounded class.

内心荒芜 2024-11-13 20:09:03

我这里的示例是使用 GHCI 8.4.4 并带有提示符 "λ: "

我认为从 Enum 派生在这里最有意义,因为 Haskell 中最基本的类型也从 Enum 派生(元组、字符、整数等),并且它有 内置方法值传入和传出枚举。

首先,创建一个派生 Enum 的数据类型(和 Show),以便您可以查看 REPL 和 Eq 中的值以启用 .. 范围补全):

λ: data MyDataType = Foo | Bar | Baz deriving (Enum, Show, Eq)
λ: [Foo ..]
[Foo,Bar,Baz]

枚举定义一个方法, fromEnum,您可以使用它来获取问题中请求的值(01 和 <代码>2)。

用法:

λ: map fromEnum [Foo ..]
[0,1,2]

定义一个给出任意值的函数是一件简单的事情(例如使用整数幂运算符^):

λ: value e = 2 ^ (fromEnum e)

用法:

λ: map value [Foo ..]
[1,2,4]

另一个答案说:

派生 Enum 解决方案很棒,但如果您想要任意映射到数字,它就不起作用。

好吧,让我们看看(如果您还没有在 GHCI 中启用多行输入,请使用 :set +m ):

arbitrary e = case e of
  Foo -> 10
  Bar -> 200
  Baz -> 3000

用法:

λ: map arbitrary [Foo ..]
[10,200,3000]

我们刚刚证明它确实有效,但我更愿意如果我们不希望值从 0 开始增加 1,请像使用 value 一样从 fromEnum 计算它。

My examples here are using GHCI 8.4.4 with a prompt, "λ: ".

I think deriving from Enum makes the most sense here, as the most fundamental types in Haskell also derive from Enum (tuples, characters, integers, etc...), and it has builtin methods of getting values into and from the enum.

First, create a data type deriving Enum (and Show so you can view the value in the REPL and Eq to enable .. range completion):

λ: data MyDataType = Foo | Bar | Baz deriving (Enum, Show, Eq)
λ: [Foo ..]
[Foo,Bar,Baz]

Enums define a method, fromEnum, which you can use to get the values as requested in the question (0, 1, and 2).

Usage:

λ: map fromEnum [Foo ..]
[0,1,2]

It is a simple matter to define a function giving an arbitrary value (such as powers of two using the integer power operator, ^):

λ: value e = 2 ^ (fromEnum e)

Usage:

λ: map value [Foo ..]
[1,2,4]

Another answer says:

The deriving Enum solution is great, but it won't work if you want to have an arbitrary mapping to numbers.

Well, let's see about that (use :set +m to enable multiline input in GHCI, if you haven't already):

arbitrary e = case e of
  Foo -> 10
  Bar -> 200
  Baz -> 3000

Usage:

λ: map arbitrary [Foo ..]
[10,200,3000]

We just demonstrated that it does indeed work, but I would prefer to calculate it from the fromEnum as we did with value, if we do not want values increasing by 1 from 0.

浅黛梨妆こ 2024-11-13 20:09:03

既然你说这些数字不是由任何常规法则生成的,那么你可以使用通用编程(例如使用 Scrap Your Boilerplate)或 Template Haskell 来实现此问题的通用解决方案。我倾向于更喜欢 Template Haskell,因为它实际上生成代码并编译它,因此您可以获得 GHC 的所有类型检查和优化优势。

如果有人已经实现了这个,我不会感到惊讶。这应该是微不足道的。

Since you say the numbers are not generated by any regular law, you could use generic programming (e.g. with Scrap Your Boilerplate) or Template Haskell to implement a generic solution to this problem. I tend to prefer Template Haskell because it actually generates code and compiles it, so you get all the type-checking and optimisation benefits of GHC.

I wouldn't be surprised if someone had implemented this already. It should be trivial.

久隐师 2024-11-13 20:09:02
data MyDataType = Foo | Bar | Baz deriving (Enum)
data MyDataType = Foo | Bar | Baz deriving (Enum)
丘比特射中我 2024-11-13 20:09:02
instance Enum MyDataType where
    fromEnum = fromJust . flip lookup table
    toEnum = fromJust . flip lookup (map swap table)
table = [(Foo, 0), (Bar, 1), (Baz, 2)]
instance Enum MyDataType where
    fromEnum = fromJust . flip lookup table
    toEnum = fromJust . flip lookup (map swap table)
table = [(Foo, 0), (Bar, 1), (Baz, 2)]
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文