我有一堆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
...
这是很多重复。什么是干涸的正确方法?
答案 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。