(编辑)如何在没有IO的情况下在Haskell中获取随机数 [英] (Edited) How to get random number in Haskell without IO

查看:100
本文介绍了(编辑)如何在没有IO的情况下在Haskell中获取随机数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想拥有一个在没有IO的情况下每次调用都返回不同的stdGen的函数. 我尝试使用unsafePerformIO作为以下代码.

import System.IO.Unsafe
import System.Random

myStdGen :: StdGen
myStdGen = unsafePerformIO getStdGen

但是当我尝试在ghci中调用myStdGen时,我总是得到相同的值.我滥用了unsafePerformIO吗?还是有其他方法可以实现我的目标?

编辑 抱歉,我应该更准确地描述我的问题.

实际上,我正在实现treap数据结构的变体,这需要特殊的合并"操作.它依靠一定的随机性来保证摊销O(log n)的预期时间复杂度.

我尝试使用像(Tree, StdGen)这样的对来保持每个诱因的随机生成器.将新数据插入挖土机时,我将使用random为新节点提供随机值,然后更新生成器.但是我遇到了一个问题.我有一个名为empty的函数,该函数将返回一个空陷阱,并且我使用了上面的函数myStdGen来获取此陷阱的随机生成器.但是,如果我有两个空洞,它们的StdGen将是相同的.因此,在我将数据插入到treap之后,以及当我想将它们合并时,它们的随机值也将相同.因此,我失去了依靠的随机性.

这就是为什么我想拥有一个某种方式的全局"随机生成器,该生成器为每个调用生成不同的StdGen,以便每个空陷阱可以具有不同的StdGen.

解决方案

这不是unsafePerformIO的好用法.

您在GHCi中反复看到相同数字的原因是GHCi本身不知道该值是不纯的,因此会记住您上次调用它时的值.您可以在GHCi的顶层中键入IO命令,因此,如果仅键入getStdGen,您将期望看到不同的值.但是,由于GHCi的工作方式中不包含涉及不还原顶级表达式的部分内容,因此这也不起作用.您可以使用:set +r来解决此问题:

> :set +r
> getStdGen
2144783736 1
> getStdGen
1026741422 1

请注意,您伪装成纯函数的不纯函数仍然无法正常工作.

> myStdGen
480142475 1
> myStdGen
480142475 1
> myStdGen
480142475 1

您真的不想走这条路. unsafePerformIO不应以这种方式使用,也不是一个好主意.有一些方法可以获取您想要的东西(例如unsafePerformIO randomIO :: Int),但它们不会带您去做美好的事情.相反,您应该基于随机monad中的随机数进行计算,并在IO monad中运行.

更新

我从您的更新中看到您为什么首先要这么做.

此答案中,在具有参照透明性的函数中,关于随机性问题有许多有趣的想法. /a>.

尽管有些人主张在这种情况下使用unsafePerformIO,但由于该页各部分概述的许多原因,它仍然是一个坏主意.最后,如果函数依赖于随机性源,则最好在其类型中指定该函数,而最简单的方法是将其放在随机monad中.这仍然是一个纯函数,只是一个在调用时带有生成器的函数.您可以在IO主例程中随机生成一个生成器.

可以在此处找到有关如何使用随机monad的一个很好的例子.. >

I want to have a function that return different stdGen in each call without IO. I've tried to use unsafePerformIO, as the following code.

import System.IO.Unsafe
import System.Random

myStdGen :: StdGen
myStdGen = unsafePerformIO getStdGen

But when I try to call myStdGen in ghci, I always get the same value. Have I abused unsafePerformIO? Or is there any other ways to reach my goal?

EDIT Sorry, I think I should describe my question more precisely.

Actually, I'm implementing a variation of the treap data strutcure, which needs a special 'merge' operation. It relies on some randomness to guarentee amortized O(log n) expected time complexity.

I've tried to use a pair like (Tree, StdGen) to keep the random generator for each treap. When inserting a new data to the treap, I would use random to give random value to the new node, and then update my generator. But I've encountered a problem. I have a function called empty which will return an empty treap, and I used the function myStdGen above to get the random generator for this treap. However, if I have two empty treap, their StdGen would be the same. So after I inserted a data to both treap and when I want to merge them, their random value would be the same, too. Therefore, I lost the randomness which I relies on.

That's why I would like to have a somehow "global" random generator, which yields different StdGen for each call, so that each empty treap could have different StdGen.

解决方案

This is not a good use of unsafePerformIO.

The reason you see the same number repeatedly in GHCi is that GHCi itself does not know that the value is impure, and so remembers the value from the last time you called it. You can type IO commands into the top level of GHCi, so you would expect to see a different value if you just type getStdGen. However, this won't work either, due to an obscure part of the way GHCi works involving not reverting top-level expressions. You can turn this of with :set +r:

> :set +r
> getStdGen
2144783736 1
> getStdGen
1026741422 1

Note that your impure function pretending to be pure will still not work.

> myStdGen
480142475 1
> myStdGen
480142475 1
> myStdGen
480142475 1

You really do not want to go down this route. unsafePerformIO is not supposed to be used this way, and nor is it a good idea at all. There are ways to get what you wanted (like unsafePerformIO randomIO :: Int) but they will not lead you to good things. Instead you should be doing calculations based on random numbers inside a random monad, and running that in the IO monad.

Update

I see from your updatee why you wanted this in the first place.

There are many interesting thoughts on the problem of randomness within otherwise referentially transparent functions in this answer.

Despite the fact that some people advocate the use of unsafePerformIO in this case, it is still a bad idea for a number of reasons which are outlined in various parts of that page. In the end, if a function depends on a source of randomness it is best for that to be specified in it's type, and the easiest way to do that is put it in a random monad. This is still a pure function, just one that takes a generator when it is called. You can provide this generator by asking for a random one in the main IO routine.

A good example of how to use the random monad can be found here.

这篇关于(编辑)如何在没有IO的情况下在Haskell中获取随机数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆