【问题标题】:Haskell pattern match on tuple length in instances实例中元组长度的 Haskell 模式匹配
【发布时间】:2016-09-04 00:10:53
【问题描述】:

我想创建一个数据类型,它可以采用长度为 2 或 3 的元组,并且应该包含派生 Num 和 Ord 的类型。然后,我想在我的类型类实例中对这个元组的长度进行模式匹配,以便我可以根据元组长度决定使用哪个函数。看看下面的不可编译的伪代码。至少有两个问题:

  1. Dominates 不是可派生类

  2. 我不知道如何获取元组,例如调用fitnesses,在实例中进行模式匹配时,将变量从数据类型中取出。

代码:

data Fits = I2 (Int, Int) | F2 (Float, Float) | I3 (Int, Int, Int) | F3 (Float, Float, Float)

data Ind = Ind { fitnesses :: Fits
               , otherInfo :: String
               } deriving (Dominates)

class Dominates a where
  dominates :: a -> a -> bool

instance Dominates Ind where
  dominates x@(_,_) y@(_,_) = x `dominates2` y -- takes two tuples of length 2
  dominates x@(_,_,_) y@(_,_,_) = x `dominates3` y -- takes two tuples of length 3

更新:

data Ind = Ind { fitnesses :: Fits
               , otherInfo :: String
               }

instance Eq Ind where
  (Ind{ fitnesses = I2 x@(a1,a2) }) == (Ind{ fitnesses = I2 y@(b1,b2) }) = indCmp2 x y == EQ
instance Ord Ind where
  (Ind{ fitnesses = I2 x@(a1,a2) }) `compare` (Ind{ fitnesses = I2 y@(b1,b2) }) = indCmp2 x y

indCmp2 :: (Num a, Ord a) => (a, a) -> (a, a) -> Ordering
indCmp2 x y
  | a0 < b0 = LT
  | a0 > b0 = GT
  -- Can assume (fst x) == (fst y) beneath
  | a1 < b1 = LT
  | a1 > b1 = GT
  | a1 == b1 = EQ
  where
    a0 = fst x
    a1 = snd x
    b0 = fst y
    b1 = snd y

这编译。如果我只想要Ord,为什么还要创建Eq 的实例?如果没有 Eq-instance,编译器会抱怨“没有 (Eq Ind) 的实例”。

【问题讨论】:

    标签: haskell pattern-matching


    【解决方案1】:

    您可以为 2 元组和 3 元组提供实例。

    class Dominates a where
      dominates :: a -> a -> bool
    
    instance (Num a, Num b) => Dominates (a,b) where
      dominates = dominates2 -- takes two tuples of length 2
    
    instance (Num a, Num b, Num c) => Dominates (a,b,c) where
      dominates = dominates3 -- takes two tuples of length 3
    

    但是,我想知道这是否是您真正需要的。

    您可能需要对所有可能的情况进行模式匹配,如下所示。这里不需要任何课程。

    dominates :: Ind -> Ind -> Bool
    dominates (Ind{ fitnesses = I2 (a1,a2) })
              (Ind{ fitnesses = I2 (b1,b2) }) = ...
    dominates (Ind{ fitnesses = F2 (a1,a2) })
              (Ind{ fitnesses = F2 (b1,b2) }) = ...
    dominates (Ind{ fitnesses = I3 (a1,a2,a3) })
              (Ind{ fitnesses = I3 (b1,b2,b3) }) = ...
    dominates (Ind{ fitnesses = F3 (a1,a2,a3) })
              (Ind{ fitnesses = F3 (b1,b2,b3) }) = ...
    dominates _ _ = error "incompatible Ind values!"
    

    例子:

    data Ind = Ind { fitnesses :: Fits
                   , otherInfo :: String
                   }  -- no deriving Ord here, we want to define it manually
    
    instance Ord Ind where
      (Ind{ fitnesses = I2 x }) `compare` (Ind{ fitnesses = I2 y }) = indCmp2 x y
      -- other cases here
    

    【讨论】:

    • 第一个例子没有提取Ind记录的fitness字段。有没有办法做到这一点,有实例?我也想为Ord 派生一个实例。
    • @tsorn 我不明白。如果x 是Ind 类型的值,则fitness x 是该字段的值。为什么需要在这里使用类?
    • 在第一个示例中,Dominates 没有Ind 的实例,因此Ind 无法派生它。第二个例子工作得很好,但我想如果我想派生Ord,我需要使用实例。我在顶帖中添加了另一个示例。
    • 很好,但是编译器抱怨我需要创建一个实例(Eq Ind)。看看更新的帖子。你知道为什么吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-13
    • 2015-04-07
    • 2017-07-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多