作为一个学习Haskell的Java人,我已经习惯了新的思维方式,但是我花了半天时间尝试用一个简单的RNG来实现一些东西,但却一无所获。在Java中,我可以对静态RNG进行机箱化,并使用Classname.random.nextInt(10)调用它,它将满足以下条件:
到目前为止,在Haskell,我正面临着典型的程序员困境--我可以有2/3。我仍然在学习,对Monad一无所知,只是他们可能会在这里帮助我。
我最近的尝试是:
getRn :: (RandomGen g) => Int -> Int -> Rand g Int
getRn lo hi= getRandomR (lo,hi)-编辑:整理我的问题,这样它就不会太长,取而代之的是一个摘要,然后我做的是:
在创建了一组随机城市(用于TSP)之后,我用一个函数createEdges映射了它们,该函数将一个城市与其他城市连接起来:M.mapWithKey (\x y -> (x,(createEdges y [1..3] makeCountry)))
问题:
我想用随机的东西代替1.3。也就是说,我想将随机性(IO)映射到纯代码之上。这给我带来了无穷无尽的困惑(见下面人们试图回答我,以便对我的困惑有一个好的感觉)。事实上,我仍然不确定我是否正确地解释了这个问题。
我得到了这类错误:Couldn't match expected type [Int] with actual type IO [Int]
解决办法:
因此,在发现我想做的事情在功能环境中是根本错误之后,我决定改变我的方法。我没有生成一个城市列表,然后应用随机性将它们连接起来,而是创建了一个[Int],其中每个内部列表都表示随机边。因此,在流程开始时就创建了我的随机性,而不是试图在纯代码上映射随机性。
(我把最终结果作为我自己的答案发布了,但还不让我接受自己的答案。一旦我达到了这个门槛,我就会回来接受)
发布于 2014-07-01 12:41:07
编辑:经过更多的阅读和朋友的额外帮助,我终于把它简化为这个解决方案。不过,我也会保留原来的解决方案,以防同样的方法帮助像我这样的新手(这也是我学习过程中的一个重要部分)。
-- Use a unique random generator (replace <$> newStdGen with mkStdGen 123 for testing)
generateTemplate = createCitiesWeighted <$> newStdGen
-- create random edges (with weight as pair) by taking a random sized sample of randoms
multiTakePair :: [Int] -> [Int] -> [Int] -> [[(Int,Int)]]
multiTakePair ws (l:ls) is = (zip chunka chunkb) : multiTakePair remaindera ls remainderb
where
(chunkb,remainderb) = splitAt l is
(chunka,remaindera) = splitAt l ws
-- pure version of utilizing multitake by passing around an RNG using "split"
createCitiesWeighted :: StdGen -> [[(Int,Int)]]
createCitiesWeighted gen = take count result
where
(count,g1) = randomR (15,20) gen
(g2,g3) = split g1
cs = randomRs (0, count - 2) g1
es = randomRs (3,7) g2
ws = randomRs (1,10) g3
result = multiTakePair ws es cs最初的解决方案
除了@user2407038的深刻评论外,我的解决方案非常依赖于我从这两个问题中读到的内容:
(注:我遇到了一个问题,我无法解决如何随机设置每个城市有多少边缘,@AnrewC提供了一个很棒的回答,它不仅回答了这个问题,而且大大减少了过多的代码)
module TspRandom (
generateCityTemplate
) where
import Control.Monad (liftM, liftM2) -- promote a pure function to a monad
-- @AndrewC's suggestion
multiTake :: [Int] -> [Int] -> [[Int]]
multiTake (l:ls) is = chunk : multiTake ls remainder
where (chunk,remainder) = splitAt l is
-- Create a list [[Int]] where each inner int is of a random size (3-7)
-- The values inside each inner list max out at 19 (total - 1)
createCities = liftM (take 20) $ liftM2 multiTake (getRandomRs (3,7)) (getRandomRs (0, 19))
-- Run the generator
generateCityTemplate = do
putStrLn "Calculating # Cities"
x <- createCities
print x
return ()发布于 2014-07-01 05:35:26
如果你愿意的话,你可以在没有任何单数或IO的情况下处理随机数。你要知道的是,由于有状态(随机数发生器的内部状态),你必须带着这个状态。
在我看来,这方面最简单的框架是Sytem.Random。
使用它,您的getRn函数可能如下所示:
getRn :: (RandomGen g) => Int -> Int -> g -> (Int, g)
getRn lo hi g = randomR (lo,hi) g在这里,您可以将g视为我前面提到的状态--将其放入其中,您将得到另一个类似于此的返回(在ghci中):
> let init = mkStdGen 11
> let (myNr, nextGen) = getRn 1 6 init
> myNr
6
> let (myNr, nextGen') = getRn 1 6 nextGen
> myNr
4我认为您可以从只使用这个线程gen开始,然后,当您得到所有的单点内容回来,使它更容易写/读。
我不知道您的数据的定义,但下面是一个使用此技术的简单示例:
module StackOQuestion where
import System.Random
getRn :: (RandomGen g) => Int -> Int -> g -> (Int, g)
getRn lo hi = randomR (lo,hi)
getRnList :: (RandomGen g) => (g -> (a, g)) -> Int -> g -> ([a], g)
getRnList f n g
| n <= 0 = ([], g)
| otherwise = let (ls, g') = getRnList f (n-1) g
(a, g'') = f g'
in (a:ls, g'')
type City = (Int, Int)
randomCity :: (RandomGen g) => g -> (City, g)
randomCity g =
let (f, g') = getRn 1 6 g
(s, g'') = getRn 1 6 g'
in ((f, s), g'')
randomCities :: (RandomGen g) => (Int, Int) -> g -> ([City], g)
randomCities (minC, maxC) g =
let (count, g') = getRn minC maxC g
in getRnList randomCity count g'你可以这样测试它:
> let init = mkStdGen 23
> randomCities (2,6) init
([(4,3),(1,2)],394128088 652912057)如您所见,这会创建两个城市(这里简单地表示为整数对)--对于init的其他值,您将得到其他答案。
如果您以正确的方式看待这个问题,您可以看到已经有了一个状态单体的开始( g -> ('a, g)部分) ;)
PS:mkStdGen有点像你从Java和co那里知道的随机初始化(你通常把你的系统时钟的滴答计数放进去),我选择11,因为它打字速度快;当然,如果你坚持使用11,你总是会得到相同的数字-所以你需要用IO中的一些东西来初始化这个包,但是你可以把这个包推到main上,如果你只是传递给g,你就可以保持纯净。
发布于 2014-07-01 22:33:25
我想说,如果您想使用随机数,最简单的方法就是使用像Control.Monad.Random这样的实用程序库。
更有教育意义的,工作密集的途径是学习写你自己的单子那样。首先,您想要理解State单曲,并对它感到舒服。我认为学习这个老问题 (免责声明:我在那里有一个答案)可能是一个研究这个问题的好起点。接下来,我要做的就是能够自己编写State monad。
在此之后,我将尝试的下一个练习是编写一个用于随机数生成的“实用程序”monad。所谓“实用程序”monad,我的意思是,monad基本上是用一个API重新打包标准State monad,从而使它更容易完成特定的任务。Control.Monad.Random包是这样实现的:
-- | A monad transformer which adds a random number generator to an
-- existing monad.
newtype RandT g m a = RandT (StateT g m a)它们的RandT monad实际上只是一个newtype定义,它重用StateT并添加一些实用函数,以便您可以集中精力使用随机数,而不是状态monad本身。因此,在这个练习中,您基本上使用您想要的API设计一个随机数生成单元,然后使用State和Random库来实现它。
https://stackoverflow.com/questions/24502918
复制相似问题