【问题标题】:How to write the instance for Show of a given datatype shorter?如何为给定数据类型的 Show 编写更短的实例?
【发布时间】:2020-06-03 18:46:06
【问题描述】:

我对 Haskell 很陌生,但我想知道如何才能更短地编写以下代码:

data Suite = Club | Heart | Spade | Diamond 

data Value = Two | Three | Four | Five | Six | Seven | Eight | Nine | Ten | Jack | Queen | 
King | Ace

data Card = Card Suite Value

instance Show Suite where
    show Club = "Club"
    show Heart = "Heart"
    show Spade = "Spade"
    show Diamond = "Diamond"

instance Enum Suite where
    enumFromTo Club Diamond = [Club, Heart, Spade, Diamond]
    enumFromTo Heart Diamond = [Heart, Spade, Diamond]
    enumFromTo Club Spade = [Club, Heart, Spade]

instance Show Value where
    show Two = "Two"
    show Three = "Three"
    show Four = "Four"
    show Five = "Five"
    show Six = "Six"
    show Seven = "Seven"
    show Eight = "Eight"
    show Nine = "Nine"
    show Ten = "Ten"
    show Jack = "Jack"
    show Queen = "Queen"
    show King = "King"
    show Ace = "Ace"

我想为 Show Value 编写更短的实例。有没有好的方法可以做到这一点,还是我需要全部写出来? 我还想知道如果我想为 Eq Card、Ord Card 定义相同的实例,我该如何从这里开始? 目前为止

instance Eq Card where
    Card _ _ == _ = False

instance Ord Card where
    Card Heart Three > Card Heart Two = True

工作,但写出每一种可能性将是相当多的工作。 感谢您的任何回答!

编辑:我知道附加派生(显示等)的可能性,但我不想使用它

【问题讨论】:

  • 究竟为什么不想使用deriving
  • 只需在Card 上折腾deriving (Show, Eq, Ord) 就可以很好地解决它。
  • deriving 正是为了节省大量工作,避免出错,而且对于某些类型类,它是Safe Haskell唯一允许的实例。
  • 更好地了解haskell
  • 您可以使用Generics 来派生构造函数名称,但这会导致一些额外(且相当复杂)的逻辑。

标签: haskell


【解决方案1】:

您已经拒绝了deriving 这些实例,这是我们避免那么多样板文件的主要方式。缩短Show Value 的最明显的剩余基本方法是使用case 表达式。这使用了额外的一行,但会稍微缩短每个案例:

instance Show Value where
    show x = case x of
      Two -> "Two"
      Three -> "Three"
      -- etc.

扩展到非基本的方式,你可以

  1. 使用泛型,可以是GHC.Generics 中更现代的版本,或者(在这种情况下可能更容易)Data.Data 中的那个。对于这些,您将分别需要deriving Genericderiving Data,然后您可以编写(或挖掘Hackage)您需要的类方法的通用版本。这两种方法似乎都不太适合 Haskell 初学者,但您可以在几个月内逐步完成。

  2. 使用模板 Haskell。这是一个非常高级的语言特性,尽管使用 Haskell 多年,但我还没有真正开始掌握如何使用它进行编程。祝你好运!

【讨论】:

  • 我想说 Template Haskell 与其说是一个非常高级的功能,不如说是乏味、不安全且非常不习惯 - Haskell 的“嘿,我们也可以做 Lisp 可以做的!”黑客。泛型更高级,不那么乏味(仍然相当乏味),但更安全且更惯用。
  • 对于那些试图“更好地了解 Haskell [a]”的人,我不能真诚地推荐 Template Haskell 泛型。
  • @AndrewRay,正是我的观点。学习如何以枯燥的方式手工编写它们,然后尽可能推导出它们。
  • @leftaroundabout 你能推荐一个合理的最新模板 Haskell 教程吗?
【解决方案2】:

如果您只希望您的 show 方法调用打印构造函数的名称(如此处所示),则根本不需要手动实例化它们。这样您就可以自动派生Show 实例:

data Suit = Club | Heart | Spade | Diamond
    deriving Show

data Value = Two | Three | Four | Five | Six | Seven 
    | Eight | Nine | Ten | Jack | Queen | King | Ace
    deriving Show

【讨论】:

  • Enum 也可以导出。
【解决方案3】:

某些 情况下,例如Instance Show Value,没有deriving(不包括dfeuer 的答案中的那些)就没有好方法来缩短它。

但在其他人那里有!例如。

  1. 对于Enum 实例,定义fromEnumtoEnum 就足够了,其余的都有默认定义。您当然不需要像示例代码那样在enumFromTo 中列出所有可能性。

  2. 定义instance Enum Value后,可以编写比较函数,转换为Int,比较结果:

    instance Eq Value where
      x == y = fromEnum x == fromEnum y
    
    instance Ord Value where
      compare x y = compare (fromEnum x) (fromEnum y)
    
  3. 您可以在为Card 编写定义时使用ValueSuit 的实例,例如

    instance Eq Card where
      Card s1 v1 == Card s2 v2 = s1 == s2 && v1 == v2
    

【讨论】:

    猜你喜欢
    • 2018-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-18
    • 2013-10-05
    相关资源
    最近更新 更多