使用QuickCheck从字符串池生成随机字符串

时间:2016-08-17 07:11:51

标签: haskell monads monad-transformers quickcheck state-monad

考虑从一组可能的字符串生成字符串的问题,这样一旦选择了字符串,就不能再重复它。对于此任务,我想使用QuickCheck的{​​{1}}函数。

如果我查看我正在尝试编写的函数的类型,它看起来非常像状态monad。因为我在州monad中使用另一个monad,即Gen。我使用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) 的第一个元素。其次,我的最终目标是为JSON值定义一个随机生成器,它使用资源池(不仅包含字符串)。使用stringGenS让我实现StateT的{​​{1}} QuickCheckelements等“有状态”变体。

我想知道是否有更好的方法来实现这一点,或者这种复杂性是定义现有monad的状态变体所固有的。

1 个答案:

答案 0 :(得分:1)

#!/usr/bin/python """ Animation of Elastic collisions with Gravity author: Jake Vanderplas email: vanderplas@astro.washington.edu website: http://jakevdp.github.com license: BSD Please feel free to use and modify this, but keep the above information. Thanks! """ import numpy as np import matplotlib.pyplot as plt import matplotlib.animation as animation N_step = 2 N_particle = 4 class ParticleBox: """Orbits class init_state is an [N x 4] array, where N is the number of particles: [[x1, y1, vx1, vy1], [x2, y2, vx2, vy2], ... ] bounds is the size of the box: [xmin, xmax, ymin, ymax] """ def __init__(self, init_state = [[1, 0, 0, -1], [-0.5, 0.5, 0.5, 0.5], [-0.5, -0.5, -0.5, 0.5]], bounds = [-2, 2, -2, 2], size = 0.04): self.init_state = np.asarray(init_state, dtype=float) self.size = size self.state = self.init_state.copy() self.time_elapsed = 0 self.bounds = bounds def step(self, dt): """step once by dt seconds""" self.time_elapsed += dt **x,y = [], [] with open("traj.xyz") as f: lines = f.readlines() Data = lines[(N_particle + 1)*self.time_elapsed:N_particle+(N_particle + 1)*self.time_elapsed] for line in Data: row = line.split() x.append(row[0]) y.append(row[1]) # update positions self.state[:, 0] = x self.state[:, 1] = y #------------------------------------------------------------ # set up initial state np.random.seed(0) init_state = -0.5 + np.random.random((4, 4)) box = ParticleBox(init_state, size=0.04) dt = 1. #------------------------------------------------------------ # set up figure and animation fig = plt.figure() fig.subplots_adjust(left=0, right=1, bottom=0, top=1) axes = fig.add_subplot(111, aspect='equal') particles, = axes.plot([], [], 'bo') rect = plt.Rectangle(box.bounds[::2], box.bounds[1] - box.bounds[0], box.bounds[3] - box.bounds[2], ec='none', lw=2, fc='none') axes.add_patch(rect) axes.grid() axes.relim() axes.autoscale_view(True,True,True) #print x,y def init(): """initialize animation""" global box, rect particles.set_data([], []) rect.set_edgecolor('none') return particles, rect def animate(i): """perform animation step""" global box, rect, dt, axes, fig box.step(dt) # update pieces of the animation rect.set_edgecolor('k') particles.set_data(box.state[:, 0], box.state[:, 1]) particles.set_markersize(10) return particles, rect #plt.draw()????? ani = animation.FuncAnimation(fig, animate, frames=3, interval=10, blit=True, init_func=init) #ani.save('particle_box.mp4', fps=30, extra_args=['-vcodec', 'libx264']) plt.show() StateT的组合可能如下所示:

Gen

问题在于,当您需要状态时,在共享状态时,您无法在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)