如何重构代码以使用MonadRandom [英] How to refactor code to use MonadRandom

查看:96
本文介绍了如何重构代码以使用MonadRandom的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 MonadRandom 。我把它放到了 randomPref 函数中,但是整个事情在之后都会出现!

  module AgentGenerator其中

导入System.Random
导入数据.Hashable
import Control.Monad.Random

导入System.Environment

- 生成代理并写入文件
- 'fname' - 输出文件名
- 's' - 代理集数量
- 'n' - 集合中代理的数量
- 'x' - 每个代理的首选项数量
生成fname snx = do
writeFile fname $ show $ generateAgentSets snx

- Agent类型:Name,首选项列表
data Agent = Agent String [Double]派生Show
类型AgentSet = [Agent]

- 每个生成'''代理'集合','x'首选项每个
generateAgentSets :: Integer - >整数 - >整数 - > [AgentSet]
generateAgentSets s n x = [generateAgents n x | i< - [1..s]]

- 使用'x'首选项生成n个代理每个
generateAgents :: Integer - >整数 - > AgentSet
generateAgents n x = [createAgent(show i)x | i< - [1..n]]

- 使用'x'首选项创建代理'名称'
createAgent :: String - >整数 - > Agent
createAgent name x =代理名称prefs其中
prefs = [randomPref(i + hashed)| i < - [1..x]]其中
hashed = fromIntegral(散列名称)

- 基于种子$ b $在[0,1]之间生成单个随机值b - TODO:摆脱种子的东西,用MonadRandom代替
randomPref ::(RandomGen g)=>整数 - > Rand g [Double]
randomPref seed = getRandomR(0.0,1.0)


解决方案

您已将代理定义为

  data Agent = Agent String [Double] 

但是在 createAgent ,你正试图使用​​类型构造一个 Agent

 代理字符串[Rand g [Double]] 

另一类错误是在 randomPref ,签名表示您正在生成一个随机双打列表,但您只生成一个双精度值。我也不是100%确定这个函数应该如何工作,因为你从不在任何地方使用种子值。你要么返回一个 Rand monad或者取一个种子值并用它来产生一个普通的double。两者都没有意义。



这是一个使用种子的版本,并且返回一个普通的double

  randomPref :: Integer  - > Double 
randomPref seed = evalRand(getRandomR(0.0,1.0))g其中
g = mkStdGen(fromIntegral seed)

我以 System.Random 为例,使用 mkStdGen 作为例子,但是您可能希望用其他一些 RandomGen



的其他实例替换它。但是,上述内容是一个相当可疑的用法 MonadRandom 除非用特定的种子生成每个代理真的很重要,否则实现 randomPref 可能更符合逻辑

  randomPref :: RandomGen g => Rand g Double 
randomPref = getRandomR(0.0,1.0)

现在我们不采取在种子值中,我们只是声明 randomPref 是一个随机double。但是,你不能只使用一个随机double,因为它是一个常规double,所以我们也需要改变其他函数。首先, createAgent

  createAgent :: RandomGen g =>字符串 - > Int  - >代理
createAgent名称x =代理名称< $> prefs其中
prefs = replicateM x randomPref

我们更改签名以反映我们实际上是返回一个随机的 Agent < $> 运算符来自模块 Control.Applicative ,它用于应用期待a普通值为 Rand 值。这只是编写 fmap(代理名称)前缀的更好方法。



prefs 是根据 replicateM (来自模块Control.Monad)定义的,它将一次性值复制x次,因此您可以获得x个随机首选项。另一方面,我已经将所有函数改为使用 Int 值而不是整数。除非您真的想要生成数十亿代理,否则这会使代码更快,并且许多标准库函数(如 replicateM )仅接受机器整数。

  generateAgents :: RandomGen g => Int  - > Int  - > Rand g AgentSet 
generateAgents nx = mapM(\ i - > createAgent(show i)x)[1..n]

generateAgents 以类似方式更改。我们在签名中注意到,我们返回一个随机 AgentSet ,并将列表理解改为 mapM mapM 与标准的 map 函数类似,只不过它适用于返回一元值的函数(比如<$ c

  generateAgentSets :: RandomGen g => Int  - > Int  - > Int  - > Rand g [AgentSet] 
generateAgentSets snx = replicateM s(generateAgents nx)

generateAgentSets 遵循相同的例程。我们用 replicateM 替换了列表理解来生成随机代理集合的实例。



最大的改变是在 generate 函数中需要

  generate :: RandomGen g => FilePath  - > Int  - > Int  - > Int  - > g  - > IO()
生成fname snxg = do
let randomSets = generateAgentSets snx
agentSets = evalRand randomSets g
writeFile fname $ show agentSets

我们需要传入一个随机数生成器,然后与 evalRand 一起使用来转换 Rand AgentSet 值转换为可以写入磁盘的普通 AgentSet 值。



为了更好地理解为什么我们需要 fmap / < $> 作为 mapM replicateM 而不是普通的旧列表解析,您可能需要阅读第11章 Chapter 12 from Learn you Gre的Haskell很好


I am trying to use MonadRandom. I put it into randomPref function, but the whole thing brows up afterwards! Any tips are appreciated.

module AgentGenerator where

import System.Random
import Data.Hashable
import Control.Monad.Random

import System.Environment

-- Generate agents and write to a file
-- 'fname' - output filename
-- 's' - number of agent sets
-- 'n' - number of agents in a set
-- 'x' - number of preferences per agent
generate fname s n x = do
    writeFile fname $ show $ generateAgentSets s n x

-- Agent type: Name, List of preferences
data Agent = Agent String [Double] deriving Show
type AgentSet = [Agent]

-- Generate 's' sets of 'n' agents each, with 'x' preferences each
generateAgentSets:: Integer -> Integer -> Integer -> [AgentSet]
generateAgentSets s n x  = [generateAgents n x | i <- [1..s] ]

-- Generate n agents with 'x' preferences each
generateAgents:: Integer -> Integer -> AgentSet
generateAgents n x = [createAgent (show i) x | i <- [1..n]]

-- Create agent 'name' with 'x' preferences
createAgent:: String -> Integer -> Agent
createAgent name x = Agent name prefs where
    prefs = [ randomPref (i + hashed) | i <- [1..x] ]  where
        hashed = fromIntegral ( hash name )

-- Generate single random value between [0, 1] based on the seed
-- TODO: Get rid of the seed thing and use MonadRandom instead
randomPref :: (RandomGen g) => Integer -> Rand g [Double]
randomPref seed = getRandomR (0.0, 1.0)

解决方案

You've defined Agent as

data Agent = Agent String [Double]

But in createAgent, you are trying to construct an Agent using the types

Agent String [Rand g [Double]]

Another type error is that in randomPref, the signature says that you are generating a list of random doubles, but you only generate one double. I'm also not 100% sure how the function should work, given that you never use the seed value anywhere. You either want to return a Rand monad or take a seed value and use that to generate a plain double. Having both doesn't really make sense.

Here's a version that uses a seed, and returns a plain double

randomPref :: Integer -> Double
randomPref seed = evalRand (getRandomR (0.0, 1.0)) g where
    g = mkStdGen (fromIntegral seed)

I've used mkStdGen from System.Random here as an example, but you might want to replace it with some other instance of RandomGen.

However, the above is a pretty questionable use of MonadRandom and unless it's really important to generate each agent with a specific seed, it's probably more logical to implement randomPref like this

randomPref :: RandomGen g => Rand g Double
randomPref = getRandomR (0.0, 1.0)

Now we don't take in a seed value, we just declare that randomPref is a random double. However, you can't just use a random double like it was a regular double, so we need to change the other functions too. First, createAgent

createAgent:: RandomGen g => String -> Int -> Rand g Agent
createAgent name x = Agent name <$> prefs where
    prefs = replicateM x randomPref

We change the signature to reflect the fact that we are actually returning a random Agent. The <$> operator is from the module Control.Applicative and it is used to apply a function expecting a plain value to a Rand value. This is just a nicer way to write fmap (Agent name) prefs.

prefs is defined in terms of replicateM (from the module Control.Monad), which replicates a monadic value x times, so you get x random prefs. On another note, I've changed all the functions to use Int values instead of Integers. Unless you really want to generate billions of agents, this makes the code a lot faster, and many standard library functions (like replicateM) only accept machine ints.

generateAgents:: RandomGen g => Int -> Int -> Rand g AgentSet
generateAgents n x = mapM (\i -> createAgent (show i) x) [1..n]

generateAgents is changed in a similar way. We note in the signature that we are returning a random AgentSet, and change the list comprehension into mapM. mapM is like the standard map function, except that is works with functions that return monadic values (such as Rand).

generateAgentSets:: RandomGen g => Int -> Int -> Int -> Rand g [AgentSet]
generateAgentSets s n x  = replicateM s (generateAgents n x)

generateAgentSets follows the same routine. We've replaced the list comprehension with replicateM to generate s instances of random agent sets.

The biggest change is required in the generate function

generate :: RandomGen g => FilePath -> Int -> Int -> Int -> g -> IO ()
generate fname s n x g = do
    let randomSets = generateAgentSets s n x
        agentSets  = evalRand randomSets g
    writeFile fname $ show agentSets

We need to pass in a random number generator, which is then used with evalRand to turn the Rand AgentSet values into plain AgentSet values that can then be written to disk.

To get a better understanding why we need fmap/<$> and functions such as mapM and replicateM instead of plain old list comprehensions, you might want to read Chapter 11 and Chapter 12 from Learn you a Haskell for Great Good.

这篇关于如何重构代码以使用MonadRandom的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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