看看下面的代码,尤其是第一个函数randVectors
。
它创建了一个随机向量的向量。为了做到这一点,随机生成器g
必须是循环的'上;返回并传递用于创建第一个向量的生成器以生成下一个向量,依此类推。代码编译并按预期工作,但这是编写Haskell的惯用方法吗?
如您所见,折叠中的步进功能忽略了折叠的内容。它可以用递归来实现,但至少在我看来这似乎并没有那么好。
import Control.Monad as M
import Control.Monad.ST
import qualified Data.Vector as V
import qualified Data.Vector.Generic as VG
import Data.Vector.Generic.Mutable as VGM
import qualified Data.Vector.Unboxed as VU
import System.Random
randVectors :: (RandomGen g) => Int -> Int -> g -> (V.Vector (VU.Vector Int), g)
randVectors nvecs veclen g =
foldl step (V.empty, g) [0..nvecs]
where
step :: (RandomGen g) => (V.Vector (VU.Vector Int), g) -> Int -> (V.Vector (VU.Vector Int), g)
step (li, g1) _ =
(V.snoc li subli, g2)
where
(subli, g2) = randVector veclen g1
randVector :: (RandomGen g) => Int -> g -> (VU.Vector Int, g)
randVector n =
shuffle vector $ VU.length vector
where
vector = VU.enumFromN 0 n
shuffle :: (RandomGen g, VG.Vector v a) => v a -> Int -> g -> (v a, g)
shuffle vec size g =
runST $ do
vec_mut <- VG.thaw vec
let swap_random g1 i = do
let (j,g2) = randomR (0,i) g1
VGM.swap vec_mut i j
return g2
g' <- M.foldM swap_random g [1..size-1]
vec' <- VG.unsafeFreeze vec_mut
let vec_sample = VG.take size vec'
return (vec_sample, g')
答案 0 :(得分:4)
如果你无法击败他们,请加入他们。 如果你不能折叠它们,展开它们。
确实,可以在这里使用unfoldrN
:
randVectors :: (RandomGen g) => Int -> Int -> g -> (V.Vector (VU.Vector Int), g)
randVectors nvecs veclen = unfoldrN nvecs (Just . randVector veclen)
更详细:
unfoldrN :: Int -> (b -> Maybe (a, b)) -> b -> Vector a
用作
unfoldrN :: Int -> (g -> Maybe (VU.Vector Int, g)) -> g -> Vector (VU.Vector Int)
Int
是生成的矢量的最大长度,即nvecs
。
函数(g -> Maybe (VU.Vector Int, g))
与randVector
完全相同,只是可以选择通过返回Nothing
来提前停止生成 - 我们不需要它,因此我们使用{{ 1}}。参数Just
是初始生成器。
或者,可以在类型中使用状态monad来表示g
重复出现的模式,然后利用
g -> (g, _)