Sat*_*vik 2 haskell quickcheck
我试图使用quickcheck生成给定函数的随机参数(假设它的所有类型都有Arbitrary实例和Show实例)以及对这些参数的函数的评估.我只需要打印参数的值并在之后评估答案.所以我期待一个具有以下类型的函数
randomEvaluate :: Testable a => a -> IO ( [String] -- arguments
, String ) -- Answer after evaluating
-- IO is just needed to get a new random number generator. If I pass a generator then I think probably I will not need IO here.
Run Code Online (Sandbox Code Playgroud)
我仍然不确定这里的类型,但我认为Testable a
会这样做.我仍然无法真正得到我需要的东西.我在快速检查数据类型的乱七八糟的所有困惑Rose
,Result
等等.
UPDATE
假设我有一个功能
add :: Int -> Int -> Int
add a b = a+b
Run Code Online (Sandbox Code Playgroud)
然后我假设一个行为
> randomEvaluate add
(["1","3"],"4")
Run Code Online (Sandbox Code Playgroud)
其中1和3是生成的随机值Int
,4是f 1 3
.
我不认为你可以使用许多快速检查代码中除了模块Test.QuickCheck.Arbitrary
和Test.QuickCheck.Gen
.
下面是一些简单的代码,仅提供一个参数所需的函数:
import Test.QuickCheck.Arbitrary
import Test.QuickCheck.Gen
import System.Random
randomEvaluate :: (Arbitrary a, Show a, Show b) => (a -> b) -> IO (String, String)
randomEvaluate f = do
stdGen <- newStdGen
let x = unGen arbitrary stdGen 1000
let y = f x
return (show x, show y)
Run Code Online (Sandbox Code Playgroud)
在这里你可以看到它的实际效果:
*Main> randomEvaluate (\(a,b) -> a + b)
("(-292,-655)","-947")
*Main> randomEvaluate (\(a,b) -> a + b)
("(586,-905)","-319")
*Main> randomEvaluate (\(a,b) -> a + b)
("(547,-72)","475")
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,如果您发现它,可以将它与具有多个参数的函数一起使用.如果这还不够,那么事情会变得有点困难,但是应该可以使用某种类型的技巧.
这是一种需要"仅"将函数的返回值包装在newtype中的方法.(对于非Haskell98功能,这可能是可以避免的):
class RandEval a where
randomEvaluate :: StdGen -> a -> ([String], String)
newtype Ret a = Ret a
instance Show a => RandEval (Ret a) where
randomEvaluate _ (Ret x) = ([], show x)
instance (Show a, Arbitrary a, RandEval b) => RandEval (a -> b) where
randomEvaluate stdGen f = (show x : args, ret)
where (stdGen1, stdGen2) = split stdGen
x = unGen arbitrary stdGen1 1000
(args, ret) = randomEvaluate stdGen2 (f x)
doRandomEvaluate :: RandEval a => a -> IO ([String], String)
doRandomEvaluate f = do
stdGen <- newStdGen
return $ randomEvaluate stdGen f
Run Code Online (Sandbox Code Playgroud)
在这里看到它:
*Main> doRandomEvaluate (\a b -> Ret (a && b))
(["False","True"],"False")
*Main> doRandomEvaluate (\a b -> Ret (a + b))
(["944","758"],"1702")
*Main> doRandomEvaluate (\a b c -> Ret (a + b + c))
(["-274","413","865"],"1004")
*Main> doRandomEvaluate (\a b c d -> Ret (a + b + c + d))
(["-61","-503","-704","-877"],"-2145")
Run Code Online (Sandbox Code Playgroud)
如果还不希望明确标记返回值,这可行,但使用语言扩展:
{-# LANGUAGE FlexibleInstances, UndecidableInstances, OverlappingInstances #-}
import Test.QuickCheck.Arbitrary
import Test.QuickCheck.Gen
import System.Random
import Control.Arrow
class RandEval a where
randomEvaluate :: StdGen -> a -> ([String], String)
instance (Show a, Arbitrary a, RandEval b) => RandEval (a -> b) where
randomEvaluate stdGen f = first (show x:) $ randomEvaluate stdGen2 (f x)
where (stdGen1, stdGen2) = split stdGen
x = unGen arbitrary stdGen1 1000
instance Show a => RandEval a where
randomEvaluate _ x = ([], show x)
doRandomEvaluate :: RandEval a => a -> IO ([String], String)
doRandomEvaluate f = do
stdGen <- newStdGen
return $ randomEvaluate stdGen f
Run Code Online (Sandbox Code Playgroud)
这是发布的原始用例:
*Main> doRandomEvaluate ( (+) :: Int -> Int -> Int )
(["-5998437593420471249","339001240294599646"],"-5659436353125871603")
Run Code Online (Sandbox Code Playgroud)
但现在你正处于GHC如何解决重叠实例的奇思妙想.例如,即使使用这个很好的(但也是非Haskell98)实例来显示布尔函数:
type BoolFun a = Bool -> a
instance Show a => Show (BoolFun a) where
show f = "True -> " ++ show (f True) ++ ", False -> " ++ show (f False)
aBoolFun :: Bool -> BoolFun Bool
aBoolFun x y = x && y
Run Code Online (Sandbox Code Playgroud)
您没有看到此实例在使用中doRandomEvaluate
:
*Main> doRandomEvaluate aBoolFun
(["False","False"],"False")
Run Code Online (Sandbox Code Playgroud)
使用原始解决方案,您可以:
*Main> doRandomEvaluate (Ret . aBoolFun)
(["False"],"True -> False, False -> False")
*Main> doRandomEvaluate (Ret . aBoolFun)
(["True"],"True -> True, False -> False")
Run Code Online (Sandbox Code Playgroud)
但请注意,这是一个滑坡.对上面代码的一个小改动,它在GHC 7.6.1中停止工作(但仍然适用于GHC 7.4.1):
instance (Show a, Arbitrary a, RandEval b) => RandEval (a -> b) where
randomEvaluate stdGen f = (show x:args, ret)
where (stdGen1, stdGen2) = split stdGen
x = unGen arbitrary stdGen1 1000
(args, ret) = randomEvaluate stdGen2 (f x)
Run Code Online (Sandbox Code Playgroud)
SPJ解释了为什么这不是一个真正的错误 - 对我来说一个明显的迹象表明这种方法正在推动类型类hackery有点太过分了.