How to use 'oneof' in quickCheck (Haskell)
-
I am trying to write a prop that changes a Sudoku and then checks if it's still valid.
However, I am not sure how to use the "oneof"-function properly. Can you give me some hints, please?
prop_candidates :: Sudoku -> Bool prop_candidates su = isSudoku newSu && isOkay newSu where newSu = update su aBlank aCandidate aCandidate = oneof [return x | x <- candidates su aBlank] aBlank = oneof [return x | x <- (blanks su)]
Here are some more info...
type Pos = (Int, Int) update :: Sudoku -> Pos -> Maybe Int -> Sudoku blanks :: Sudoku -> [Pos] candidates :: Sudoku -> Pos -> [Int] [return x | x <- (blanks example)] :: (Monad m) => [m Pos]
I have struggeled with this prop for 3 hours now, so any ideas are welcome!
What I was driving at is that you have a type mix-up. Namely,
aBlank
is not aPos
, but aGen Pos
, soupdate su aBlank aCandidate
makes no sense! In fact, what you want is a way to generate a new sudoku given an initial sudoku; in other words a functionsimilarSudoku :: Sudoku -> Gen Sudoku
Now we can write it:
similarSudoku su = do aBlank <- elements (blanks su) -- simpler than oneOf [return x | x <- blanks su] aCandidate <- elements (candidates su aBlank) return (update su aBlank aCandidate)
or even simpler:
similarSudoku su = liftM2 (update su) (elements (blanks su)) (elements (candidates su aBlank))
And the property looks like
prop_similar :: Sudoku -> Gen Bool prop_similar su = do newSu <- similarSudoku su return (isSudoku newSu && isOkay newSu)
Since there are instances
Testable Bool Testable prop => Testable (Gen prop) (Arbitrary a, Show a, Testable prop) => Testable (a -> prop)
Sudoku -> Gen Bool
isTestable
as well (assuminginstance Arbitrary Sudoku
).On my blog, I wrote a simple craps simulator with QuickCheck tests that use
oneof
to generate interesting rolls.Say we have a super-simple Sudoku of a single row:
module Main where import Control.Monad import Data.List import Test.QuickCheck import Debug.Trace type Pos = Int data Sudoku = Sudoku [Char] deriving (Show)
No super-simple Sudoku should have repeated values:
prop_noRepeats :: Sudoku -> Bool prop_noRepeats s@(Sudoku xs) = trace (show s) $ all ((==1) . length) $ filter ((/='.') . head) $ group $ sort xs
You might generate a super-simple Sudoku with
instance Arbitrary Sudoku where arbitrary = sized board :: Gen Sudoku where board :: Int -> Gen Sudoku board 0 = Sudoku `liftM` shuffle values board n | n > 6 = resize 6 arbitrary | otherwise = do xs <- shuffle values let removed = take n xs dots = take n $ repeat '.' remain = values \\ removed ys <- shuffle $ dots ++ remain return $ Sudoku ys values = ['1' .. '9'] shuffle :: (Eq a) => [a] -> Gen [a] shuffle [] = return [] shuffle xs = do x <- oneof $ map return xs ys <- shuffle $ delete x xs return (x:ys)
The
trace
is there to show the randomly generated boards:*Main> quickCheck prop_noRepeats Sudoku "629387451" Sudoku "91.235786" Sudoku "1423.6.95" Sudoku "613.4..87" Sudoku "6..5..894" Sudoku "7.2..49.." Sudoku "24....1.." [...] +++ OK, passed 100 tests.
it seems that
aBlank :: Gen Pos
which does not match the way it is used as an argument ofcandidates :: Sudoku -> Pos -> [Int]
.I've been looking through here to find a way to convert
Gen a
toa
which would allow you to use it with candidates. The best i could see is thegenerate
function.Tell me if I'm missing something...
来源:https://stackoverflow.com/questions/1828850/how-to-use-oneof-in-quickcheck-haskell