简体   繁体   English

Haskell快速检查生成并测试玫瑰树?

[英]Haskell quickcheck to generate and test rose trees?

I am trying out a simple rose tree code. 我正在尝试一个简单的玫瑰树代码。

data RoseT a = Leaf a | Node a [RoseT a] deriving (Show)

instance Eq (RoseT a) where
 (==) (Leaf a) (Leaf b) = a == b
 (==) (Node a rs1) (Node b rs2) = and ((a==b): (zipWith (==) rs1 rs2))
 (==) _ _ = False 

Can I use quickcheck to test the implementation of Eq instance ? 我可以使用quickcheck来测试Eq实例的实现吗? if yes, how? 如果有,怎么样? if no, what is best alternative? 如果不是,最好的选择是什么?

I also have a function that does the following: 我还有一个功能,可以执行以下操作:

appendPath :: (RoseT a) -> (RoseT (a,[a]))
appendPath rst = appendPath' [] rst 

appendPath :: [a] -> (RoseT a) -> (RoseT (a,[a]))
appendPath' p (Leaf a) = Leaf (a,p)
appendPath' p (Node a rs) = Node (a,p) (map (appendPath' (a:p)) rs)

The function appendPath takes a rose-tree as input and returns a rose-tree with the path from node to root in every node of the tree. 函数appendPath将玫瑰树作为输入,并返回一个玫瑰树,其中包含树中每个节点中从节点到根的路径。 This information I use in another function. 我在另一个函数中使用的这些信息。

How do I use quickcheck to check this implementation on large sized rose-trees? 如何使用quickcheck在大型玫瑰树上检查此实现?

EDIT: As suggested by mhwombat, it seems impossible to write generator that takes type parameter as an argument. 编辑:正如mhwombat所建议的,似乎不可能编写将类型参数作为参数的生成器。 So here is my type parameter. 所以这是我的类型参数。 I want to construct a RoseT of strings representing valid random arithmetic expressions, where, the arithmetic expressions themselves follow the following structure: 我想构造一个表示有效随机算术表达式的字符串RoseT ,其中,算术表达式本身遵循以下结构:

data Expr = Var String | AddExpr [Expr] | MulExpr [Expr] deriving Show

So, is there a way to generate random RoseT Expr where the Expr themselves are randomly generated using quickcheck only? 那么,有没有办法生成随机RoseT Expr ,其中Expr本身是仅使用quickcheck随机生成的?

Thanks again mhwombat, for bearing with my baby-steps. 再次感谢mhwombat,我的宝贝步骤。

Unless I'm missing something, your Eq implementation of RoseT is the same as the default derived implementation. 除非我遗漏了某些内容,否则RoseTEq实现与默认的派生实现相同。 So you can just say 所以你可以说

data RoseT a = Leaf a | Node a [RoseT a] deriving (Show, Eq)

and forget the instance Eq (RoseT a) where stuff. 并忘记instance Eq (RoseT a) where东西。

The next question is whether or not this will meet your needs for testing. 接下来的问题是这是否符合您的测试需求。 If you're testing with a floating-point type parameter, eg, RoseT Double , then you need to allow for differences due to rounding. 如果您正在使用浮点类型参数进行测试,例如RoseT Double ,那么您需要考虑因舍入而产生的差异。 In that case what you need is a function that compares two trees and sees if the values are "close enough". 在这种情况下,您需要的是一个比较两个树并查看值是否“足够接近”的函数。

However, I suspect your RoseT implementation won't depend in any way on the type parameter. 但是,我怀疑你的RoseT实现不会以任何方式依赖于type参数。 In that case, you could just test it with a nice simple type like Char or Int , and use == for any comparisons you need. 在这种情况下,你可以用一个很好的简单类型测试它,比如CharInt ,并使用==进行你需要的任何比较。

You have two type signatures for appendPath . appendPath有两种类型的签名。 I think the second one was supposed to be appendPath' : 我认为第二个应该是appendPath'

appendPath' :: [a] -> (RoseT a) -> (RoseT (a,[a]))

Now for how to test it. 现在如何测试它。 It would be best if you/QuickCheck have some control over the complexity of the trees being tested. 如果你/ QuickCheck能够控制被测树的复杂性,那将是最好的。 This will help you because the simplest trees will be tested first, so you find bugs "early" (ie, with simpler test cases that are easier to debug). 这将对您有所帮助,因为最先测试最简单的树,因此您可以“早期”发现错误(即,更简单的测试用例更容易调试)。 You can do this by implementing a "sized" generator for your class. 您可以通过为您的班级实施“大小”生成器来实现此目的。 Here's one way to do it. 这是一种方法。 The higher the value of the "size" parameter, the more levels the tree is likely to have. “size”参数的值越高,树可能具有的级别就越多。

type TestRoseT = RoseT Char

sizedArbTestRoseT :: Int -> Gen TestRoseT
sizedArbTestRoseT 0 = do
  c <- arbitrary
  return $ Leaf c
sizedArbTestRoseT n = do
  c <- arbitrary
  subtreeCount <- choose (0,n-1)
  subtrees <- vectorOf subtreeCount (sizedArbTestRoseT (n-1))
  return $ Node c subtrees

instance Arbitrary TestRoseT where
  arbitrary = sized sizedArbTestRoseT

prop_appendPath_does_something :: TestRoseT -> Property
prop_appendPath_does_something t = undefined -- stub

We can sample the test data that's generated like so: 我们可以对生成的测试数据进行采样,如下所示:

λ> sample (sizedArbTestRoseT 2)
Node '\a' [Node '\RS' []]
Node '?' []
Node '\158' []
Node 'o' [Node 'E' []]
Node '\196' []
Node '4' [Node 'G' []]
Node ';' [Node 'f' []]
Node 'A' [Node '\CAN' []]
Node '!' []
Node 'q' [Node '\t' []]
Node '\'' [Node '\212' []]

Edit: 编辑:

For your Expr type, we can write a generator like so: 对于您的Expr类型,我们可以像这样编写一个生成器:

sizedArbExpr :: Int -> Gen Expr
sizedArbExpr 0 = do
  s <- arbitrary
  return $ Var s
sizedArbExpr n = do
  es <- vectorOf 2 (sizedArbExpr (n-1))
  elements [AddExpr es, MulExpr es]

instance Arbitrary Expr where
  arbitrary = sized sizedArbExpr

We'll need to modify TestRoseT and its generator so that the complexity of the tree is consistent with the "size" parameter: 我们需要修改TestRoseT及其生成器,以便树的复杂性与“size”参数一致:

type TestRoseT = RoseT Expr

sizedArbTestRoseT :: Int -> Gen TestRoseT
sizedArbTestRoseT 0 = do
  c <- sizedArbExpr 0 -- changed this to keep complexity in bounds
  return $ Leaf c
sizedArbTestRoseT n = do
  c <- sizedArbExpr (n-1) -- changed this to keep complexity in bounds
  subtreeCount <- choose (0,n-1)
  subtrees <- vectorOf subtreeCount (sizedArbTestRoseT (n-1))
  return $ Node c subtrees

Testing these modifications gives something like: 测试这些修改会产生类似于:

λ> sample (sizedArbTestRoseT 3)
Node (MulExpr [MulExpr [Var "",Var ""],AddExpr [Var "",Var ""]]) [Node (MulExpr [Var "",Var ""]) [Node (Var "") []]]
Node (MulExpr [AddExpr [Var "",Var ""],AddExpr [Var "",Var ""]]) [Node (AddExpr [Var "",Var ""]) []]
Node (MulExpr [AddExpr [Var "\164D",Var "\151\246\FS"],MulExpr [Var ":\149j\EM",Var "h\253\255"]]) [Node (MulExpr [Var "\CAN\a\ACK",Var "\184"]) [Node (Var "t\154]\\") []],Node (MulExpr [Var "\135",Var "\f\DEL\\"]) [Node (Var "\SOH\DEL") []]]
Node (AddExpr [AddExpr [Var "",Var ""],MulExpr [Var "Kj\STXV",Var "D\141<s\187"]]) []
Node (AddExpr [MulExpr [Var "\252",Var "`"],MulExpr [Var "\167`t\196",Var ":\135\NULdr\237\167"]]) []
Node (AddExpr [MulExpr [Var "]\173\&28D\SOCom",Var "^\196\ETB2\216\&2\GS\ENQ\ENQ"],AddExpr [Var "$bB\212\SOH\146\234",Var "\DC3\213\&3\SUB\\}^\246(\200"]]) [Node (MulExpr [Var "l;\133\EM\147#\SUBN\\\t",Var "\235\151U\129m3|"]) [Node (Var "\NULb\133") []],Node (AddExpr [Var "\187\EOT\165S\207\r\"\RS",Var "4"]) []]
Node (MulExpr [MulExpr [Var "%0eK",Var "`N**k\FS6\NAK"],MulExpr [Var "'lUL\NAKRPc\ENQR",Var "j\232+`\FS@n"]]) [Node (AddExpr [Var "H\DC1C%\DC48<\t\ETX.L",Var "\235+\v\STXX,\NAK\SUBQc="]) [Node (Var "f\254oX?w\224\195~/") []]]
Node (AddExpr [AddExpr [Var "P",Var "\148G\STX\DEL*\136(\161\159\&7"],AddExpr [Var "\218\136-",Var "9?\128\159\b\b%3t}\131qe"]]) [Node (MulExpr [Var "\198\249\&4\176\193/}\DC28",Var ")Gl0ym\GS"]) [Node (Var ")\204\226qA\175") []]]
Node (MulExpr [MulExpr [Var "\t\186r.",Var "j\ENQ\183\NUL\NAK\129:rg[\170o\157g\238"],AddExpr [Var "\218F\226\248\156\225\&1",Var "vu\SOH\138+CKW\EM\167\&1n"]]) [Node (MulExpr [Var ",\241\158={o\182\"5\t\STX\ETX\DC2\218\162",Var "\197\&1"]) [Node (Var "u?a};\238") []]]
Node (MulExpr [MulExpr [Var "*",Var "R"],AddExpr [Var "\CAN8C",Var "\232V.\172ILy\162a"]]) []
Node (MulExpr [MulExpr [Var "\SI\240NF\249-\v$",Var "K`\STX\231w{"],MulExpr [Var "\DC1\255\209",Var "/\227\146\236\STX\185T3r\f"]]) [Node (MulExpr [Var "\229,\DLE\NAKwf[7P\160\DEL",Var "\134#\RS\SI0KCg\195\NAK\"\191\&6\243\193\SI"]) [Node (Var "\226\&7b8\f\EOTgF\171\GS}\189c\SUBc\ETX") []]]

By the way, you said "it seems impossible to write generator that takes type parameter as an argument". 顺便说一句,你说“似乎不可能编写以类型参数为参数的生成器”。 Actually it is possible to do that, however I don't think that's what you really want here. 实际上有可能做到这一点,但我不认为这就是你真正想要的。

Incidentally, it seems a bit unusual that you want to create a tree ( RoseT ) where the leaves contain binary trees ( Expr ). 顺便说一句,你想要创建一个树( RoseT ),其中叶子包含二叉树( Expr )似乎有点不寻常。 In other words, you're creating trees of trees. 换句话说,你正在创造树木。 Of course, I don't know your application. 当然,我不知道你的申请。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM