首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Haskell -简单构造函数比较(?)函数

Haskell -简单构造函数比较(?)函数
EN

Stack Overflow用户
提问于 2012-04-12 03:36:57
回答 6查看 2.6K关注 0票数 24

在我的项目中,我创建了一个数据类型,它可以包含以下几种类型的值之一:

代码语言:javascript
复制
data PhpValue = VoidValue | IntValue Integer | BoolValue Bool

我现在想做的是,有一种简单的方法来检查PhpValue类型的两个值是否具有相同的构造函数(如果我对这里的术语感到困惑,请纠正我,但基本上我想检查的是,例如,这两个值是否都是IntValue,而不关心特定的值)。

下面是我为此编写的函数:

代码语言:javascript
复制
sameConstructor :: PhpValue -> PhpValue -> Bool
sameConstructor VoidValue VoidValue = True
sameConstructor (IntValue _) (IntValue _) = True
sameConstructor (BoolValue _) (BoolValue _) = True
sameConstructor _ _ = False

这可以正常工作,但我真的不喜欢它:如果我添加更多的构造函数(如FloatValue Float),我将不得不重写函数,并且随着我的数据定义变得更大,它将变得更大。

这个问题:有没有一种方法可以编写这样的函数,这样当我添加更多的构造函数时,它的实现就不会改变?

需要说明的是:我不想更改data的定义,我的其余代码中已经有足够的Monad了;)

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2012-04-12 03:44:34

看看Data.Data和它的toConstr函数。这将返回构造函数的表示形式,可以比较该构造函数是否相等。

通过扩展(您可以将{-# LANGUAGE DeriveDataTypeable #-}放在模块的顶部),您可以自动派生一个Data实例:

代码语言:javascript
复制
data PhpValue = VoidValue | IntValue Integer | BoolValue Bool 
              deriving (Typeable, Data)

然后,您应该能够使用toConstr函数按构造函数进行比较。

现在,以下情况将为真:

代码语言:javascript
复制
toConstr (BoolValue True) == toConstr (BoolValue False)

使用Data.Function中的on,您现在可以将sameConstructor重写为:

代码语言:javascript
复制
sameConstructor = (==) `on` toConstr

这与

代码语言:javascript
复制
sameConstructor l r = toConstr l == toConstr r

我认为使用on的版本更容易阅读。

票数 23
EN

Stack Overflow用户

发布于 2012-04-12 03:52:37

这在Haskell和ML系列语言中被称为expression problem;有许多不令人满意的解决方案(包括使用Data.Typeable和滥用类型类,在Haskell中),但没有很好的解决方案。

票数 5
EN

Stack Overflow用户

发布于 2017-07-30 12:51:57

由于定义遵循常规格式,因此您可以使用模板Haskell为任何数据类型自动派生此类函数。我继续写了一个simple package,因为我对现有的解决方案并不完全满意。

首先,我们定义一个类

代码语言:javascript
复制
class EqC a where
    eqConstr :: a -> a -> Bool
    default eqConstr :: Data a => a -> a -> Bool
    eqConstr = (==) `on` toConstr

然后是一个函数deriveEqC :: Name -> DecsQ,它将自动为我们生成实例。

default是一个default signature,这意味着当该类型是Data的一个实例时,我们可以省略eqConstr的定义,而回到Tikhon的实现。

模板Haskell的好处是它产生了一个更有效的函数。我们可以编写$(deriveEqC ''PhpValue)并获得一个实例,这正是我们手动编写的实例。看一下生成的核心:

代码语言:javascript
复制
$fEqCPhpValue_$ceqConstr =
  \ ds ds1 ->
    case ds of _ { 
      VoidValue ->
        case ds1 of _ { 
          __DEFAULT -> False;
          VoidValue -> True
        };  
      IntValue ds2 ->
        case ds1 of _ { 
          __DEFAULT -> False;
          IntValue ds3 -> True
        };  
      BoolValue ds2 ->
        case ds1 of _ { 
          __DEFAULT -> False;
          BoolValue ds3 -> True
        }   
    }  

相比之下,使用Data会引入大量额外的间接性,因为在比较它们是否相等之前,会为每个参数实例化一个显式的Constr

代码语言:javascript
复制
eqConstrDefault =
  \ @ a $dData eta eta1 ->
    let {
      f
      f = toConstr $dData } in
    case f eta of _ { Constr ds ds1 ds2 ds3 ds4 ->
    case f eta1 of _ { Constr ds5 ds6 ds7 ds8 ds9 ->
    $fEqConstr_$c==1 ds ds5
    }
    }

(在计算toConstr的过程中还有很多其他不值得展示的臃肿的东西)

在实践中,这导致模板Haskell实现的速度大约是原来的两倍:

代码语言:javascript
复制
benchmarking EqC/TH
time                 6.906 ns   (6.896 ns .. 6.915 ns)
                     1.000 R²   (1.000 R² .. 1.000 R²)
mean                 6.903 ns   (6.891 ns .. 6.919 ns)
std dev              45.20 ps   (32.80 ps .. 63.00 ps)

benchmarking EqC/Data
time                 14.80 ns   (14.77 ns .. 14.82 ns)
                     1.000 R²   (1.000 R² .. 1.000 R²)
mean                 14.79 ns   (14.77 ns .. 14.81 ns)
std dev              60.17 ps   (43.12 ps .. 93.73 ps)
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10112733

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档