Сделайте аналогичные свойства QuickCheck DRY

У меня есть несколько свойств 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 
 ...     

Это много повторений. Каким будет правильный способ СУШКИ?


person Rudolf Adamkovič    schedule 11.04.2014    source источник


Ответы (3)


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)]
person Thomas M. DuBuisson    schedule 12.04.2014

Как насчет чего-то вроде

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

Это позволяет избежать дублирования логики. Нравится вам то, что вам нравится, или нет, решать вам :)

person Daniel Gratzer    schedule 11.04.2014

Если вы поместите эту прагму в начало файла:

{-# LANGUAGE ParallelListComp #-}

Возможно, вы сможете сделать что-то подобное с GHC:

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

Это должно сгенерировать список из трех свойств, а затем сказать, что все они должны быть истинными. Первое свойство использует f = length и x = n, второе свойство использует f = head и x = -d, а последнее свойство использует f = last и x = d.

person Toxaris    schedule 11.04.2014