2016-08-17 17 views
0

文字列を選択した後、再度繰り返すことができないように、文字列を生成するという問題を考えてみましょう。この作業のために、QuickCheckGen関数を使用したいと思います。QuickCheckを使用した文字列プールからのランダムな文字列の生成

私が書こうとしている関数のタイプを見ると、それは状態モナドのように見えます。私は別のモナド、すなわちGenを使用しているので、モナドの中にあります。私は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を使用すると、私はこれを達成するためのより良い方法があるのか​​どうか迷った、または、そのような複雑さは、既存のモナドのステートフルバリアントを定義するに固有の

など、QuickCheckさんelementslistOfの「ステートフル」の変種を実装するために私を導きました。

+0

私は、作成された 'Strings'を格納するために、あるいは少なくともシードを作成し、シード/' String'の 'Set'のメンバシップのために各シード/生成された文字列を比較します。 – 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に複数のそのような計算を実行することはできませんということです。行うための唯一の合理的な事はタイプGenState -> Gen [String]である

evalStateT (replicateM 10 stringGenS) 

と(同じ状態を使用して)一緒に複数のランダムユニークな文字列を生成することであろう。

+0

ありがとうございます。リフトの使用は間違いなくよりエレガントです。状態については、私が以前のコメントで述べたように、階層的な関係を持つデータを生成する必要があるので、1つの要素の選択は将来の選択肢を制限し、その理由を1つのジェネレータからもう片方。文字列の生成は単なる例示であった。 –

+0

「Gen」はあなたに特別な分布を与えていないことに注意してください。それは読書のために使用されるので、どんな形でも統一されているのではなく、さまざまなコーナーケースをキャッチしようとします。 –

+0

良い点。私はこれを将来的には間違いなく考慮する必要があります。私は 'ランダム'モナドを使って終わるだろう。しかし、モナド変圧器を構成するのと同じテクニックを使うことができればと思っています。 –

関連する問題