从数组或列表中选择随机元素

0 random purescript

免责声明:我使用的是 PureScript,但也添加了 Haskell 标签,因为我认为这在两种语言中的行为方式可能相同,而且 Haskell 社区更大。

我想从数组中反复选择一个随机元素。每次我都希望有一个新的随机选择,但重复调用时的值总是相同的。似乎随机函数每次运行程序只评估一次。

这在后续调用中始终返回相同的名称:

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
Run Code Online (Sandbox Code Playgroud)

使用此解决方法,它每次都会返回一个新的随机选择:

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
Run Code Online (Sandbox Code Playgroud)

我正在使用Data.ArrayEffect.RandomEffect.UnsafePartial.Unsafe

我觉得这是一个丑陋的黑客。实现这一目标的正确方法是什么?

ama*_*loy 5

每次调用时都会执行不同操作的函数与 Haskell 的设计相反,我假设 PureScript 也是如此,基于您必须导入的名称“Effect.Unsafe”。使用 Unsafe 包中的东西编写这样的函数而不“作弊”是不可能的,任何与这样的函数交互的人都会头疼。

相反,给你的函数一个更诚实的类型签名。我不知道 PureScript 的等价物,但在 Haskell 中它会是这样的(改编自Get a random list item in Haskell):

pick :: [a] -> Maybe (IO a)
pick [] = Nothing
pick xs = Just $ do
  i <- randomRIO (0, len)
  pure $ xs !! i
  where len = length xs - 1
Run Code Online (Sandbox Code Playgroud)

首先,您承认如果给定一个空列表,该函数实际上无法从列表1 中生成一个项目。然后,您承认这不是纯函数:您必须执行 IO(也许 PureScript 将其称为 Effect?)以随机选择。现在调用者知道这两种效果,并且必须处理它们:通过检查空性并将其视为 IO 操作而不是纯值。


1作为解析,不要验证争论,实际上让您的函数接受一个NonEmpty a而不是接受[a]并返回一个可能会更好,但我不想在这里引入新的依赖项。