gpt4 book ai didi

haskell - 使用 QuickCheck 评估随机参数的函数

转载 作者:行者123 更新时间:2023-12-02 16:59:36 25 4
gpt4 key购买 nike

我正在尝试使用 Quickcheck 生成给定函数的随机参数(假设其所有类型都有任意实例和 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.

我仍然不确定这里的类型,但我认为 Testable a 就可以了。我仍然无法真正得到我需要的东西。我对快速检查数据类型 RoseResult 等的困惑感到困惑。

更新

假设我有一个函数

add :: Int -> Int -> Int
add a b = a+b

然后我假设行为像

> randomEvaluate add
(["1","3"],"4")

其中 1 和 3 是为 Int 生成的随机值,4 是 f 1 3

最佳答案

我认为除了 Test.QuickCheck.ArbitraryTest.QuickCheck.Gen 模块之外,您不能使用大部分 QuickCheck 代码。

仅一个参数

下面是一些简单的代码,它提供了仅包含一个参数的函数所需的内容:

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)

在这里您可以看到它的实际效果:

*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")

正如您所看到的,如果您取消柯里化(Currying)它,则可以将它与具有多个参数的函数一起使用。如果这还不够,事情会变得有点困难,但应该可以通过一些类型类技巧来实现。

多个参数,显式标记返回类型

这是一种“仅”需要将函数的返回值包装在新类型中的方法。 (对于非 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

在这里查看它的实际效果:

*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")

具有语言扩展的多个参数

如果也不希望必须显式标记返回值,则这可以工作,但使用语言扩展:

{-# 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

这是发布的原始用例:

*Main> doRandomEvaluate ( (+) :: Int -> Int -> Int )
(["-5998437593420471249","339001240294599646"],"-5659436353125871603")

但现在您可以随心所欲地了解 GHC 如何解决重叠实例。例如。即使使用这个很好的(但也是非 Haskell98)实例来显示 bool 函数:

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

您在doRandomEvaluate中没有看到此实例正在使用:

*Main> doRandomEvaluate aBoolFun 
(["False","False"],"False")

使用原始解决方案,您可以:

*Main> doRandomEvaluate (Ret . aBoolFun)
(["False"],"True -> False, False -> False")
*Main> doRandomEvaluate (Ret . aBoolFun)
(["True"],"True -> True, False -> False")

警告

但请注意,这是一个滑坡。对上面的代码进行一个小的更改,它在 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)

SPJ explains为什么这并不是一个真正的错误——对我来说,这是一个明显的迹象,表明这种方法正在将类型类黑客技术推得太远。

关于haskell - 使用 QuickCheck 评估随机参数的函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14294802/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com