考虑从一组可能的字符串生成字符串的问题,这样一旦选择了字符串,就不能再重复它。对于此任务,我想使用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}} QuickCheck
,elements
等“有状态”变体。
我想知道是否有更好的方法来实现这一点,或者这种复杂性是定义现有monad的状态变体所固有的。
答案 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)
。