使类似的QuickCheck属性干燥

时间:2014-04-11 13:52:58

标签: haskell quickcheck

我有一堆QuickCheck属性定义如下:

 ...
 prop_scaleData3 d n = n > 1 ⇛ length (scaleData d n) ≡ n
 prop_scaleData4 d n = n > 1 ⇛ head (scaleData d n) ≡ -d
 prop_scaleData5 d n = n > 1 ⇛ last (scaleData d n) ≡ d 
 ...     

这是很多重复。什么是干涸的正确方法?

3 个答案:

答案 0 :(得分:3)

这样的东西
gt1 :: (Integer -> Prop) -> Prop
gt1 f = forAll $ \(Positive n) -> f $ n + 1

然后您的属性变为

prop_scaleData3 d = gt1 $ \n -> length (scaleData d n) ≡ n
prop_scaleData4 d = gt1 $ (≡ -d) . head . scaleData d
prop_scaleData5 d = gt1 $ (≡d)   . last . scaleData d

这避免了重复的逻辑。你是否喜欢无点的东西取决于你:)

答案 1 :(得分:2)

prop_scaleData3 d n = n > 1 ==> length (scaleData d n) == n
prop_scaleData4 d n = n > 1 ==> head (scaleData d n) == -d
prop_scaleData5 d n = n > 1 ==> last (scaleData d n) == d 

只需观察这三个函数的相似之处,并创建一个新的辅助函数来提取共性。例如:

scaleProp :: Int -> Int -> ([Int] -> Int) -> Int -> Bool
scaleProp d n op res = n > 1 ==> op (scaleData d n) == res

然后你可以用帮助者表达原始道具:

 prop_scaleData3 d n = scaleProp d n length n
 prop_scaleData4 d n = scaleProp d n head   (-d)
 prop_scaleData4 d n = scapeProp d n last   d

此时重复不是逻辑,而是语法(命名函数和应用参数)。在这种情况下,我不觉得DRY原则真的很有帮助 - 你可以减少语法重复,但是你会失去可读性或模块性。例如,Toxaris将解决方案合并为一个功能;我们可以做同样的事情,但让我们用更简单的方式做一下布尔列表:

prop_scaleData345 d n =
  let sp = scaleProp d n
  in and [sp length n, sp head (-d), sp last d]
  -- or instead:
  -- in all (uncurry sp) [(length, n), (head, negate d), (last, d)]

答案 2 :(得分:0)

如果您将此pragma放在文件的顶部:

{-# LANGUAGE ParallelListComp #-}

你可以用GHC做这样的事情:

prop_scaleData345 d n = n > 1 => conjoin
  [ f (scaleData d n) == x 
  | f <- [length, head, last]
  | x <- [n     , -d  , d   ]
  ]

这应该生成一个包含三个属性的列表,然后说它们都必须为true。第一个属性使用f = length和x = n,第二个属性使用f = head和x = -d,最后一个属性使用f = last和x = d。