2016-08-17 156 views
0

考虑从一组可能的字符串中生成字符串的问题,这样一旦选择字符串,就不能再次重复。对于这个任务,我想使用QuickCheckGen函数。使用QuickCheck从字符串池中生成随机字符串

如果我看看我正在编写的函数的类型,它看起来非常像状态monad。由于我正在使用另一个monad,即Gen,在州monad中。我使用StateT写了我的第一次尝试。

arbitraryStringS :: StateT GenState Gen String 
arbitraryStringS = 
    mapStateT stringGenS get 

其中:

newtype GenState = St {getStrings :: [String]} 
    deriving (Show) 

removeString :: String -> GenState -> GenState 
removeString str (St xs) = St $ delete str xs 

stringGenS :: Gen (a, GenState) -> Gen (String, GenState) 
stringGenS genStSt = 
    genStSt >>= \(_, st) -> 
    elements (getStrings st) >>= \str -> 
    return (str, removeString str st) 

东西困扰我的这个实现是我不使用的stringGenS第一个元素的事实。其次,我的最终目标是为JSON值定义一个随机生成器,它使用资源池(不仅包含字符串)。使用StateT促使我实现QuickCheckelementslistOf

我想知道是否有实现这一目标的一个更好的办法,或者这样的复杂性是与生俱来的,以确定现有的单子的状态变量的“状态”的变种。

+0

我会以另一种方式来做 - 存储创建的“字符串” - 或者至少是种子,并比较每个种子/生成的字符串以获得种子/字符串的“集合”中的成员资格。 – epsilonhalbe

+0

另一种选择是使用uuid来生成“最可能”唯一的字符串,如果您只有一组有限的字符串 - 最终用完字符串,可以通过组合大型基本组来解决 - 但仍然可以运行到重复的字符串 - 如果你需要“真正的唯一性”,我会去一个基本集合+像自然数字这样的无限集合并结合。 – epsilonhalbe

+0

字符串来自资源池很重要。这可用于使用某些数据库中存在的数据生成测试。 –

回答

1

StateTGen的组合看起来是这样的:

import Control.Monad.State 
import Data.List (delete) 
import Test.QuickCheck 

-- A more efficient solution would be to use Data.Set. 
-- Even better, Data.Trie and ByteStrings: 
-- https://hackage.haskell.org/package/bytestring-trie-0.2.4.1/docs/Data-Trie.html 
newtype GenState = St { getStrings :: [String] } 
    deriving (Show) 

removeString :: String -> GenState -> GenState 
removeString str (St xs) = St $ delete str xs 

stringGenS :: StateT GenState Gen String 
stringGenS = do 
    s <- get 
    str <- lift $ elements (getStrings s) 
    modify $ removeString str 
    return str 

的问题是,当你需要的状态,你不能在Gen运行多个这样的计算,同时共享的状态。唯一合理的事情会生成多个随机的唯一字符串连接在一起(使用相同的状态)

evalStateT (replicateM 10 stringGenS) 

这是GenState -> Gen [String]类型。

+0

谢谢。 'lift'的使用绝对更加优雅。关于国家,正如我在我以前的评论中提到的那样,由于我需要生成具有层次关系的数据,因此选择一个元素会限制我未来的选择,这就是为什么我想将状态从一个生成器传递到另一个。字符串的生成只是说明性的。 –

+0

请注意'Gen'不会给你任何特别的分配。由于它用于阅读,它试图捕捉各种角落案例,而不是以任何方式统一。 –

+0

好点。我肯定需要在未来考虑这一点。这可能是我最终会使用“Random”monad。不过,我希望我可以用同样的方法来编写monad变形金刚。 –