【问题标题】:Haskell: How to generate a cartesian product of two simple algebraic data typesHaskell:如何生成两种简单代数数据类型的笛卡尔积
【发布时间】:2012-12-19 11:06:09
【问题描述】:

我正在学习 Haskell,所以我正在编写一些简单的纸牌游戏。我已经定义了一些数据类型:

data Rank = Ace|Two|Three|Four|Five|Six|Seven|Eight|Nine|Ten|Jack|Queen|King deriving (Eq,Show,Ord)

data Suit = Hearts|Spades|Diamonds|Clubs deriving (Show)

data Card = Card Rank Suit 

现在我想创建一副包含 52 张卡片的原始牌组。我确信有一种巧妙的方法可以做到这一点,但我能想到的只是:

 pristineDeck = [Card Ace Hearts, Card Two Hearts, ...]

我可以让 Haskell 为我生成这个列表吗?

【问题讨论】:

  • 让你的类型派生出“Enum”类型类(只需将 Enum 放在 Show 旁边即可)。他们三个:等级,西装和卡片。
  • P.S.您正在寻找的不是叉积,而是涉及 3D 矢量的东西。您的意思可能是“笛卡尔积”。
  • @BenMillwood 我的错...“SQL 交叉联接”+“笛卡尔积”+ 物理学学位
  • @BenMillwood:最好还是稳妥行事,像数学家一样——把一切都称为“产品”,并依靠上下文来消除歧义。如果您觉得大方,也许可以将其称为“张量积”。
  • @C.A.McCann:你有金额吗?而是将其称为副产品!

标签: haskell cartesian-product algebraic-data-types


【解决方案1】:

列表推导是一种非常简洁的语法。如果你在RankSuit 上推导出Enum,你可以很简单地表达为:

pristineDeck = [ Card rank suit | suit <- [Hearts .. Clubs], rank <- [Ace .. King] ]

如果你想知道为什么我的suitrank 的顺序不同,第一个是因为Card 构造函数使用的顺序,而后者是获取结果列表的顺序--西装按升序排列。

更一般地说,或者当单个列表理解变得过于庞大时,笛卡尔积正是Monad 列表实例给出的行为。以下等价于上面的列表推导:

pristineDeck = do suit <- [Hearts .. Clubs]
                  rank <- [Ace .. King]
                  return $ Card rank suit

作为另一个小问题,为了省去记住Suit 值的顺序的麻烦,派生Bounded 也将允许编写[minBound .. maxBound] 以枚举任何类型的所有值以及两者的实例EnumBounded

【讨论】:

  • 就我个人而言,我不会为Suit 类型派生Enum,而只是在理解中写出完整的列表。西装没有自然顺序,IMO,所以省略号语法令人困惑。
  • 我还会派生一个Bounded 实例。 [minBound .. maxBound] 清楚地表明您正在枚举所有变体,即使没有自然排序。
  • @AlfonsoVillén:谢谢。愚蠢的糟糕没有好的令人头疼的语法角落案例......
  • Card &lt;$&gt; [minBound..maxBound] &lt;*&gt; [minBound..maxBound]
  • @Tinctorius:那行得通!但在这种情况下,我实际上认为列表理解更具可读性。更有用的是我在调整后的Prelude 中定义的enumerate = [minBound .. maxBound]。正是为了这种目的,它很方便。
【解决方案2】:

有几种方法可以做到这一点,不同数量的魔法。

首先,由于您的类型的构造函数都没有参数,您可以为它们派生Enum。这将允许您编写例如[Ace..King] 获取所有卡片的列表。

其次,列表推导式是形成从多个其他列表中提取的项目列表的好方法。试试这个:

[x + y | x <- [100,200,300], y <- [1,2,3]]

这应该为您提供应用于示例所需的工具。

【讨论】:

    【解决方案3】:

    Alp 告诉你派生 Enum 是正确的

    >data Rank = Ace|Two|Three|Four|Five|Six|Seven|Eight|Nine|Ten|Jack|Queen|King deriving (Eq,Show,Ord,Enum)
    >data Suit = Hearts|Spades|Diamonds|Clubs deriving (Show,Enum)
    

    现在:

    >enumFrom Ace
    [Ace,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Jack,Queen,King]
    

    要获得两个列表的排列,您可以使用列表推导:

    >[[x,y]|x<-[1..2],y<-[2..5]]
    [[1,2],[1,3],[1,4],[1,5],[2,2],[2,3],[2,4],[2,5]]
    

    或得到加法的排列:

    >[x + y|x<-[1..2],y<-[2..5]]
    [3,4,5,6,4,5,6,7]
    

    现在你只需要做一些替换就可以得到 Car with Rank and Suit 的排列。

    【讨论】:

      猜你喜欢
      • 2016-02-15
      • 2016-05-07
      • 2021-05-06
      • 2019-08-26
      • 2016-10-08
      • 1970-01-01
      • 1970-01-01
      • 2015-06-16
      • 2011-09-18
      相关资源
      最近更新 更多