Random 从数组或列表中拾取随机元素
免责声明:我使用的是PureScript,但也添加了Haskell标记,因为我认为这在两种语言中可能表现相同,并且Haskell社区更大 我想从数组中选择一个随机元素,重复地。每次我都希望有一个新的随机选择,但重复调用的值总是相同的。似乎随机函数在每次运行程序时只计算一次 这在后续调用时始终返回相同的名称:Random 从数组或列表中拾取随机元素,random,purescript,Random,Purescript,免责声明:我使用的是PureScript,但也添加了Haskell标记,因为我认为这在两种语言中可能表现相同,并且Haskell社区更大 我想从数组中选择一个随机元素,重复地。每次我都希望有一个新的随机选择,但重复调用的值总是相同的。似乎随机函数在每次运行程序时只计算一次 这在后续调用时始终返回相同的名称: import Data.Array (length, unsafeIndex) import Effect.Random (randomInt) import Effect.Unsafe (
import Data.Array (length, unsafeIndex)
import Effect.Random (randomInt)
import Effect.Unsafe (unsafePerformEffect)
import Partial.Unsafe (unsafePartial)
pick :: forall a. Array a -> a
pick arr = unsafePartial $ unsafeIndex arr i where
i = unsafePerformEffect $ randomInt 0 (length arr - 1)
name :: String
name = pick names
使用此解决方法,每次都会返回一个新的随机拾取:
import Data.Array (length, unsafeIndex)
import Effect.Random (randomInt)
import Effect.Unsafe (unsafePerformEffect)
import Partial.Unsafe (unsafePartial)
pick :: forall a. Array a -> a
pick arr = unsafePartial $ unsafeIndex arr i where
i = unsafePerformEffect $ randomInt 0 (length arr - 1)
-- without the dummy argument, this is not re-evaluated
-- on subsequent calls and always returns the same name
name :: Unit -> String
name _ = pick names
我在用,还有
我觉得这是一个丑陋的黑客。实现这一点的正确方法是什么?一个每次调用时都会执行不同操作的函数与Haskell的设计相反,基于您必须导入的名称“Effect.Unsafe”,我假设PureScript也是如此。要编写这样一个函数而不使用不安全包中的东西进行“欺骗”是不可能的,任何与这样一个函数交互的人都会头疼 相反,给你的函数一个更诚实的类型签名。我不知道PureScript的等价物,但在Haskell中,它是这样的(改编自):
pick::[a]->可能(IO a)
选择[]=Nothing
选择xs=Just$do
我你可能想在你的问题上加上“随机”标签
我不知道PureScript,对于新手来说文档似乎很少,但在Haskell圈子里,这似乎是一个相当常见的抱怨:随机数生成函数。通常关于随机数的说法适用
然而,Haskell有一个关于随机数生成的既定原则。这并不一定涉及IO,即使在Haskell中IO monad碰巧“承载”了一个随机数生成器
在Haskell中,您需要:
import System.Random
import Control.Monad.Random
问题是,给定相同参数的函数总是返回相同的结果
解决方案是,您需要将随机数生成器的初始状态作为函数参数包含,并将新的更新状态作为结果的一部分返回。这就是Haskell函数的作用。第一个参数是输出范围。如果您的数组有100个索引在0到99之间的元素,那么这将是一个2元组:(0,99)
一旦有一个函数返回一个随机值,就可以轻松地构建第二个函数返回任意数量的值,例如:
randomRn :: (RandomGen g, Random a) => (a, a) -> Int -> g -> ([a], g)
randomRn range count g0 =
if (count <= 0)
then ([], g0) -- no values and no change
else let (a0, g1) = randomR range g0
(as, gf) = randomRn range (count-1) g1 -- recursive call
in
(a0:as, gf)
程序输出:
Random indexes v1: [9,56,13,9,38,86,62,18,77,4,66,65,27,33,68,55,94,15,77,45]
现在,根据品味、风格和问题的复杂性,你可能会发现国家的明确存在令人烦恼,并想以某种方式隐藏它。为此,Haskell使用状态单子的一个变体,称为。使用这种方法,您可以使用如下代码定义返回随机值列表的一元操作:
iterateMn :: MonadRandom mr => (Int, Int) -> Int -> mr [Int]
iterateMn range count =
if (count <= 0) then
return [] -- no action required
else
do
v1 <- getRandomR range
vs <- iterateMn range (count-1)
return (v1:vs)
详情如下:
PureScript随机数生成工具似乎构建在Javascript工具之上。根据您的要求有多严格,它可能足够好,也可能不够好。例如,您可能决定咬紧牙关,实现一个PureScript版本的随机数生成器。众所周知,它的统计特性非常强,它的状态具有非常小的内存大小,因此可以灵活地适应函数式编程语言。显然,已经有几种Lisp实现可用。多亏了@amalloy的回答,我找到了我认为适合我的案例的很好的解决方案
关键是保持效应
不受随机数生成的影响(效应
对应于Haskell中的IO
),而不是使用unsafeperformefect
丢弃它<代码>效果
反映了一个事实,即该值的计算涉及到一些副作用,并且每次可能会产生不同的结果。这正是我想要的。因此,有了这个新的类型签名,它现在的行为和我预期的一样:name::Effect String
。每次效果为“运行”时,它都会从数组中随机选择一个新字符串
正如@amalloy所建议的,我现在也使用非空数组
pick :: forall a. NonEmptyArray a -> Effect a
pick arr = do
i <- randomInt 0 (length arr - 1)
let item = arr !! i
case item of
Just one -> pure one
Nothing -> pure $ head arr
-- still have to handle the Maybe from (!!) which is
-- a bit annoying since this obviously can never be Nothing
name :: Effect String
name = pick names
main :: Effect Unit
main = do
name >>= log
name >>= log
name >>= log
-- new pick each time
pick::for all a。非空射线a->效果a
选择arr=do
我是纯洁的
无->纯$head arr
--仍然需要处理来自(!!)的可能
--有点烦人,因为这显然不可能什么都不是
名称::效果字符串
名称=选择名称
主要影响单位
main=do
名称>>=日志
名称>>=日志
名称>>=日志
--每次都有新的选择
如果你真的确定i
在那里,你总是可以使用fromJust
和unsafePartial
——开玩笑:-P为了减轻负担,你可以把它变成一行:pick arr=randomInt 0(length arr-1)index arr>>fromMaybe(head arr)
。。。。又开玩笑了:-P但是说真的-我认为这个助手连同shuffle
等应该进入一些随机的实用程序库,所以可能值得在这里添加一个注释:。你怎么认为?
iterateMn :: MonadRandom mr => (Int, Int) -> Int -> mr [Int]
iterateMn range count =
if (count <= 0) then
return [] -- no action required
else
do
v1 <- getRandomR range
vs <- iterateMn range (count-1)
return (v1:vs)
let action = iterateMn range count -- monadic action object
(indexes2, gf2) = runRand action g0 -- go generate indexes
pick :: forall a. NonEmptyArray a -> Effect a
pick arr = do
i <- randomInt 0 (length arr - 1)
let item = arr !! i
case item of
Just one -> pure one
Nothing -> pure $ head arr
-- still have to handle the Maybe from (!!) which is
-- a bit annoying since this obviously can never be Nothing
name :: Effect String
name = pick names
main :: Effect Unit
main = do
name >>= log
name >>= log
name >>= log
-- new pick each time