haskell enum - 如果值构造函数需要值而不是空值,该怎么办?给出了需求场景

Posted

技术标签:

【中文标题】haskell enum - 如果值构造函数需要值而不是空值,该怎么办?给出了需求场景【英文标题】:haskell enum - what to do in case value constructors require value instead of nullary? Requirement scenario is given 【发布时间】:2012-07-24 14:26:26 【问题描述】:

LYAH 在Derived Instances 说

[...] 所有的值构造函数都是空的(不带参数,即字段),我们可以将其作为 Enum 类型类的一部分。

data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday
deriving (Eq, Ord, Show, Read, Bounded, Enum)

现在,如果我需要几个月,那就是

data month = January | February | March | April | May | June | July | August |September | October | November |December     
deriving (Eq, Ord, Show, Read, Bounded, Enum)

我的问题是:

    在哪里存储每个月的 Max Days 值? 如果是闰年,那么二月的 maxDays 是 29 天,否则是 28 天,如何提及和控制?

在 Java 中可以编写如下代码:

public enum Month   
    January (31),  
    February (29),  
    March (31),  
    April (30),  
    May (31),  
    June (30),  
    July (31),  
    August (31),  
    September (30),           
    October (31),  
    November (30),  
    December (31),  
    ;  
    private int maxDays; // instance variables  
    private (int maxDays)  // constructors always private  
           this.maxDays = maxDays;  
      
    Public int getMaxDays ()   
       return maxDays;  
    

【问题讨论】:

您应该使用time 包,它是由真正了解情况的人编写的。 您要求的不是Enum 的有效实例。 Enum 类用于可以在不丢失信息的情况下与Int 的子集相互转换的类型。您已经为几个月分配了相同的数字,这使得这种转换变得不可能。 @Optimight:在您的 Day 示例中,deriving(Enum) 子句生成Enum 的有效实例。具体来说,生成的函数每天都会给出不同的Int 值。 Monday = 0, Tuesday = 1, ... Sunday = 6. @HeatSink:在这种情况下,对于数据 Month 的第二个示例,也给出 January = 0, February = 1, ... December = 11。唯一的事情,它进一步将 maxDays 的值附加到每个月。 @Optimight,我明白了。您希望 maxDays 与构造函数 ID 分开。 【参考方案1】:

这应该可行。

data Month = January | February | March | April | May
           | June | July | August |September | October
           | November | December
           deriving (Eq, Ord, Show, Read, Bounded, Enum)

type Year = Int

isLeapYear :: Year -> Bool
isLeapYear year = year `mod` 4 == 0 && (year `mod` 100 /= 0 || year `mod` 400 == 0)

getMaxDays :: Year -> Month -> Int
getMaxDays _ January = 31

getMaxDays year February
    | isLeapYear year = 29
    | otherwise = 28

getMaxDays _ March = 31
getMaxDays _ April = 30
getMaxDays _ May = 31
getMaxDays _ June = 30
getMaxDays _ July = 31
getMaxDays _ August = 31
getMaxDays _ September = 30
getMaxDays _ October = 31
getMaxDays _ November = 30
getMaxDays _ December = 31

【讨论】:

【参考方案2】:

为什么需要Month 成为枚举?在我看来,您正试图在代码中强制采用 OO 样式,这不是一个好主意。 Java 面向对象的代码编写风格不能完全转换为 Haskell 等函数式语言。

在 OO 中,您可以将数据结构和对该数据的所有相关操作捆绑在一个类中,而在 FP 中,您可以将数据结构与相关操作分开定义。这意味着 FP 方法可以更轻松地定义对数据的新操作,而 OO 方法可以更轻松地将新信息添加到数据结构中。就我个人而言,我发现自己定义新操作比添加新字段和 FP 风格套装要多得多。

与 Haskell 中的 Java 示例最接近的类似物是定义一个 Typeclass -

data Month = January | February | March | April
           | May | June | July | August |September
           | October | November |December
  deriving (Eq, Ord, Show, Read, Bounded, Enum)

data Year = Int

class HasDays X where
  maxdays :: X -> Int
  days    :: X -> Year -> Int
  -- Any other "methods" here

instance HasDays Month where
  maxdays January = 31
  maxdays February = 29
  maxdays .. = .. -- Similar code for other months

  days February y = .. -- Leap year calculation
  days m _ = maxdays m

【讨论】:

我习惯了 OO 范式,但是在选择 FP 时,它必须是纯正的 FP 方法。

以上是关于haskell enum - 如果值构造函数需要值而不是空值,该怎么办?给出了需求场景的主要内容,如果未能解决你的问题,请参考以下文章

用于检查排列的 Haskell 函数

简单Haskell函数中的中间值

为什么我不能在不同的数据类型中重用相同的值构造函数?

枚举enum

Java 枚举(enum)的用法

Java 枚举(enum)的用法