如何在quickCheck中使用'oneof'(Haskell)

时间:2009-12-01 20:53:37

标签: haskell functional-programming ghci quickcheck

我正在尝试编写一个改变数独的道具,然后检查它是否仍然有效。

但是,我不确定如何正确使用“oneof”功能。你能给我一些提示吗?

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)]

以下是更多信息...

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]

我已经用这个道具拼了3个小时了,所以欢迎任何想法!

3 个答案:

答案 0 :(得分:5)

我所驾驶的是你有类型混淆。也就是说,aBlank不是Pos,而是Gen Pos,所以update su aBlank aCandidate毫无意义!事实上,你想要的是一种在给定初始数据的情况下生成新数独的方法;换句话说就是一个函数

similarSudoku :: Sudoku -> Gen Sudoku

现在我们可以写出来:

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)

甚至更简单:

similarSudoku su = liftM2 (update su) (elements (blanks su)) (elements (candidates su aBlank))

该属性看起来像

prop_similar :: Sudoku -> Gen Bool
prop_similar su = do newSu <- similarSudoku su
                     return (isSudoku newSu && isOkay newSu)

因为有实例

Testable Bool
Testable prop => Testable (Gen prop)
(Arbitrary a, Show a, Testable prop) => Testable (a -> prop)

Sudoku -> Gen Bool也是Testable(假设为instance Arbitrary Sudoku)。

答案 1 :(得分:2)

在我的博客上,我用QuickCheck测试编写了一个simple craps simulator,使用oneof来生成有趣的卷。

假设我们有一行超级简单的数独游戏:

module Main where
import Control.Monad
import Data.List
import Test.QuickCheck
import Debug.Trace

type Pos = Int
data Sudoku = Sudoku [Char] deriving (Show)

没有超级简单的数独应该有重复的值:

prop_noRepeats :: Sudoku -> Bool
prop_noRepeats s@(Sudoku xs) =
  trace (show s) $ all ((==1) . length) $
                   filter ((/='.') . head) $
                   group $ sort xs

你可以用

生成一个超级简单的数独游戏
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)

trace用来显示随机生成的电路板:

*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.

答案 2 :(得分:1)

似乎aBlank :: Gen Poscandidates :: Sudoku -> Pos -> [Int]的参数不一致。

我一直在浏览here,找到一种方法将Gen a转换为a,这样您就可以将其与候选人一起使用。我能看到的最好的是generate函数。

告诉我,如果我错过了什么......