免责声明:我使用的是 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.Array、Effect.Random、Effect.Unsafe和Partial.Unsafe。
我觉得这是一个丑陋的黑客。实现这一目标的正确方法是什么?
每次调用时都会执行不同操作的函数与 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]并返回一个可能会更好,但我不想在这里引入新的依赖项。
| 归档时间: |
|
| 查看次数: |
277 次 |
| 最近记录: |