首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在Haskell中使用SmallCheck?

如何在Haskell中使用SmallCheck?
EN

Stack Overflow用户
提问于 2013-05-15 01:21:14
回答 3查看 1.9K关注 0票数 16

我试图使用SmallCheck来测试Haskell程序,但是我不明白如何使用这个库来测试我自己的数据类型。显然,我需要使用Test.SmallCheck.Series。然而,我发现它的文档非常令人困惑。我对食谱式的解决方案和逻辑解释(一元?)都感兴趣。结构。以下是我的一些问题(所有相关问题):

  • 如果我有一个数据类型data Person = SnowWhite | Dwarf Integer,如何向smallCheck解释有效值是Dwarf 1通过Dwarf 7 (或SnowWhite)?如果我有一个复杂的FairyTale数据结构和一个构造函数makeTale :: [Person] -> FairyTale,并且希望smallCheck使用构造函数从人的列表中创建童话-s,该怎么办? 我成功地使quickCheck像这样工作,而不让我的手太脏,因为我将Control.Monad.liftM的明智应用程序应用于makeTale这样的函数。我想不出用smallCheck做这件事的方法(请给我解释一下!)
  • SerialSeries等类型之间的关系是什么?
  • (可选) coSeries的意义是什么?如何使用来自PositiveSmallCheck.Series类型
  • (可选)任何阐明什么是什么背后的逻辑,什么应该是一个一元表达式,什么仅仅是一个规则的函数,在smallCheck的上下文中,将不胜感激。

如果有任何介绍/教程使用smallCheck,我会感谢一个链接。非常感谢!

更新:--我应该补充说,我为smallCheck找到的最有用和最易读的文档是本文(PDF)。我第一眼就找不到问题的答案,这与其说是一本教程,不如说是一份有说服力的广告。

更新2:,我把关于在Test.SmallCheck.list和其他地方出现的奇怪的Identity的问题转移到了单独问题上。

EN

回答 3

Stack Overflow用户

发布于 2013-05-15 02:58:30

注意:这个答案描述了SmallCheck的1.0前版本。有关这篇博客文章 0.6和1.0之间的重要差异,请参见SmallCheck。

SmallCheck与QuickCheck类似,因为它在可能的类型空间的某个部分测试一个属性。不同之处在于,它试图穷尽地列举一系列“小”值,而不是小值的任意子集。

正如我所暗示的,SmallCheck的Serial就像QuickCheck的Arbitrary一样。

现在,Serial非常简单:Serial类型a有一种方法(series)来生成Series类型,这只是来自Depth -> [a]的一个函数。或者,要打开这个包,Serial对象是我们知道如何枚举一些“小”值的对象。我们还得到了一个Depth参数,它控制我们应该生成多少个小值,但是让我们忽略它一分钟。

代码语言:javascript
复制
instance Serial Bool where series _ = [False, True]
instance Serial Char where series _ = "abcdefghijklmnopqrstuvwxyz"
instance Serial a => Serial (Maybe a) where
  series d = Nothing : map Just (series d)

在这些情况下,我们只需忽略Depth参数,然后枚举每个类型的“所有”可能值。我们甚至可以对某些类型自动执行此操作。

代码语言:javascript
复制
instance (Enum a, Bounded a) => Serial a where series _ = [minBound .. maxBound]

这是一种非常简单的方法来彻底测试属性--字面上测试每一个可能的输入!显然,至少有两个主要缺陷:(1)在测试时,无限数据类型将导致无限循环;(2)嵌套类型会导致大量的示例空间可供查看。在这两种情况下,SmallCheck都会很快变得非常大。

这就是Depth参数的重点-它让系统要求我们保持我们的Series小。从文档来看,Depth

生成测试值的最大深度 对于数据值,它是嵌套构造函数应用程序的深度。 对于函数值,它既是嵌套案例分析的深度,也是结果的深度。

因此,让我们重新研究我们的例子,以保持它们的小。

代码语言:javascript
复制
instance Serial Bool where 
  series 0 = []
  series 1 = [False]
  series _ = [False, True]
instance Serial Char where 
  series d = take d "abcdefghijklmnopqrstuvwxyz"
instance Serial a => Serial (Maybe a) where
  -- we shrink d by one since we're adding Nothing
  series d = Nothing : map Just (series (d-1))

instance (Enum a, Bounded a) => Serial a where series d = take d [minBound .. maxBound]

好多了。

那么coseries是什么?就像coarbitrary在QuickCheck的Arbitrary类型中一样,它允许我们构建一系列“小”函数。注意,我们正在通过输入类型编写实例--结果类型在另一个Serial参数中传递给我们(下面是调用results)。

代码语言:javascript
复制
instance Serial Bool where
  coseries results d = [\cond -> if cond then r1 else r2 | 
                        r1 <- results d
                        r2 <- results d]

这些都需要更多的智慧来编写,实际上我将推荐您使用alts方法,我将在下面简要描述这些方法。

那么,我们如何才能做出一些Series of Persons呢?这部分很容易

代码语言:javascript
复制
instance Series Person where
  series           d = SnowWhite : take (d-1) (map Dwarf [1..7])
  ...

但是我们的coseries函数需要生成从Person到其他东西的每一个可能的函数。这可以使用altsN系列SmallCheck提供的函数来完成。这里有一种写它的方法

代码语言:javascript
复制
 coseries results d = [\person -> 
                         case person of
                           SnowWhite -> f 0
                           Dwarf n   -> f n
                       | f <- alts1 results d ]

其基本思想是altsN results从带有Serial实例的N值到ResultsSerial实例生成N-ary函数。因此,我们使用它从前面定义的Serial值0.7创建一个函数到我们需要的任何东西,然后我们将Person映射到数字并传递给它们。

现在我们有了一个用于SerialPerson实例,我们可以使用它来构建更复杂的嵌套Serial实例。对于“例如”,如果FairyTalePerson的列表,我们可以在Serial Person实例旁边使用Serial a => Serial [a]实例来轻松地创建Serial FairyTale

代码语言:javascript
复制
instance Serial FairyTale where
  series = map makeFairyTale . series
  coseries results = map (makeFairyTale .) . coseries results

( (makeFairyTale .)makeFairyTalecoseries生成的每个函数组合在一起,这有点让人费解)

票数 17
EN

Stack Overflow用户

发布于 2013-07-01 22:55:24

  • 如果我有一个数据类型data Person = SnowWhite | Dwarf Integer,如何向smallCheck解释有效值是Dwarf 1通过Dwarf 7 (或SnowWhite)?

首先,您需要决定要为每个深度生成哪些值。这里没有一个正确的答案,这取决于您希望搜索空间的粒度有多细。

以下是两种可能的选择:

  1. people d = SnowWhite : map Dwarf [1..7] (不取决于深度)
  2. people d = take d $ SnowWhite : map Dwarf [1..7] (每个深度单位增加一个元素的搜索空间)

在您决定之后,您的Serial实例非常简单

代码语言:javascript
复制
instance Serial m Person where
    series = generate people

我们在这里留下了m多态,因为我们不需要底层monad的任何特定结构。

  • 如果我有一个复杂的FairyTale数据结构和一个构造函数makeTale :: [Person] -> FairyTale,并且希望smallCheck使用构造函数从人的列表中创建童话-s,该怎么办?

使用cons1

代码语言:javascript
复制
instance Serial m FairyTale where
  series = cons1 makeTale
  • SerialSeries等类型之间的关系是什么?

Serial是一个类型类;Series是一个类型。您可以拥有相同类型的多个Series --它们对应于枚举该类型值的不同方法。然而,为每个值指定应该如何生成它可能是很困难的。Serial类允许我们为生成特定类型的值指定一个很好的缺省值。

Serial的定义是

代码语言:javascript
复制
class Monad m => Serial m a where
  series   :: Series m a

因此,它所做的就是将一个特定的Series m a分配给给定的ma组合。

  • coseries的意义是什么?

它需要生成函数类型的值。

  • 如何使用来自PositiveSmallCheck.Series类型

例如,如下所示:

代码语言:javascript
复制
> smallCheck 10 $ \n -> n^3 >= (n :: Integer)
Failed test no. 5.
there exists -2 such that
  condition is false

> smallCheck 10 $ \(Positive n) -> n^3 >= (n :: Integer)
Completed 10 tests without failure.
  • 任何阐明什么是什么背后的逻辑,什么应该是一个一元表达式,什么仅仅是一个规则的函数,在smallCheck的上下文中,将不胜感激。

在编写Serial实例(或任何Series表达式)时,您在Series m monad中工作。

在编写测试时,使用返回BoolProperty m的简单函数。

票数 4
EN

Stack Overflow用户

发布于 2013-05-15 05:19:43

虽然我认为@tel的答案是一个很好的解释(我希望smallCheck能够像他描述的那样工作),但是他提供的代码对我不起作用( smallCheck版本1)。我设法让下面的人开始工作..。

更新/警告:下面的代码是错误的,原因很微妙。有关更正的版本和详细信息,请参见下面提到的问题的这个答案。简短的版本是,不是instance Serial Identity Person,而是必须编写instance (Monad m) => Series m Person

..。但是我发现Control.Monad.Identity和所有编译器标志的使用都很奇怪,我已经询问了单独问题

还请注意,虽然Series Person (或实际上是Series Identity Person)实际上与函数Depth -> [Person]不完全相同(请参阅@tel的答案),但函数generate :: Depth -> [a] -> Series m a在它们之间进行转换。

代码语言:javascript
复制
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, FlexibleContexts, UndecidableInstances #-}
import Test.SmallCheck
import Test.SmallCheck.Series
import Control.Monad.Identity

data Person = SnowWhite | Dwarf Int

instance Serial Identity Person where
        series = generate (\d -> SnowWhite : take (d-1) (map Dwarf [1..7]))
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16555291

复制
相关文章

相似问题

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