如何在Haskell中链接二进制函数? [英] How to chain binary functions in Haskell?

查看:52
本文介绍了如何在Haskell中链接二进制函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用 System.Random.next 函数生成N个数字.我实现了一个函数,该函数需要一个 StdGen 和一个数字列表,并返回新的生成器和更新的数字列表:

 导入System.RandomgetRandNum :: StdGen->[Int]->(StdGen,[Int])getRandNum gen nums =(newGen,newNums)在哪里(randNum,newGen)=下一代newNums = nums ++ [randNum] 

然后我可以通过以下方式使用它:

 λ:getRandNum(mkStdGen 1)[](80028 40692,[39336]) 

但是我在执行该函数N次以获取随机数列表时遇到了问题.如何正确链接它?

我还尝试了递归方法-它可以工作,但是我确信此解决方案远非优雅:

  randomnumbers_ :: Int->StdGen->(StdGen,[Int])randomnumbers_ 0 gen =(gen,[])randomnumbers_ 1 gen =(newGen,[randNum])在哪里(randNum,newGen)=下一代randomnumbers_ n gen =(newGen2,nums ++ nums2)在哪里(newGen,nums)= randomnumbers_ 1 gen(newGen2,nums2)= randomnumbers_(n-1)newGenrandomnumbers :: Int->整数->[Int]随机数n种子= snd $ randomnumbers_ n生成器在哪里生成器= mkStdGen种子 

是的,我知道可以使用 State monad来实现它,而且我知道该怎么做.

解决方案

链接它的方法是以不需要执行的方式来实现它,即,它可以自己进行链接.

无论如何,您的代码中都有一个内在的矛盾,您称它为 getRandNum (单数形式),的确只有一个数字,但类型为 [Int] ./p>

因此,我们只需对代码进行最少的编辑即可解决所有这些问题,

  getRandNums :: StdGen->[Int]getRandNums gen = randNum:newNums在哪里(randNum,newGen)=下一代newNums = getRandNums newGen 

这种方案对于Haskell来说很典型,它使用自称为自卫的递归( guarded )递归来构建列表,即由惰性数据构造函数(在本例中为:).像您一样,重复添加单身人士建立的列表效率很低,在访问时具有二次行为.

newGen 安全地隐藏在内部,封装起来是一个额外的好处.您不希望它公开,这有什么用?从中间重新开始随机数生成序列将始终会重新创建相同的数字序列.

当然,您可以根据需要从列表中删除任意数量的数字,而取n .

I want to generate N numbers using System.Random.next function. I implemented a function which takes a StdGen and a list of numbers and returns new generator and updated list of numbers:

import System.Random  

getRandNum :: StdGen -> [Int] -> (StdGen, [Int])
getRandNum gen nums = (newGen, newNums) 
                where
                (randNum, newGen) = next gen
                newNums = nums ++ [randNum]

Then I can use it in the following way:

λ: getRandNum (mkStdGen 1) []
(80028 40692,[39336])

But I have a problem with executing that function N times to get the list of random numbers. How can I chain it in a proper way?

I also tried the recursive way -- it works, but I'm sure this solution is far from elegant:

randomnumbers_ :: Int -> StdGen -> (StdGen, [Int])
randomnumbers_ 0 gen = (gen, [])
randomnumbers_ 1 gen = (newGen, [randNum])
  where
    (randNum, newGen) = next gen
randomnumbers_ n gen = (newGen2, nums ++ nums2)
  where
    (newGen, nums) = randomnumbers_ 1 gen
    (newGen2, nums2) = randomnumbers_ (n - 1) newGen

randomnumbers :: Int -> Int -> [Int]
randomnumbers n seed = snd $ randomnumbers_ n generator
  where
    generator = mkStdGen seed

By the way yes, I know that it can be implemented using the State monad and I know how to do it.

解决方案

The way to chain it is to implement it in such a way that you don't need to do it, i.e. so that it does the chaining by itself.

There's an inherent contradiction in your code anyway, you call it getRandNum, singular, and it indeed gets just one number, but the type is [Int].

So then we resolve all this, with a minimal edit to your code, as

getRandNums :: StdGen -> [Int]
getRandNums gen = randNum : newNums 
                where
                (randNum, newGen) = next gen
                newNums = getRandNums newGen 

This kind of scheme is typical for Haskell, building lists in the top-down fashion using what's known as guarded recursion, i.e. with recursion guarded by a lazy data constructor (in this case, :). Lists built with repeated appending of singletons as you do are very inefficient, have quadratic behavior when accessed.

Having newGen safely hidden inside, encapsulated, is an additional bonus. You don't want it exposed, what would be the use? Restarting the randoms generating sequence from the middle would just recreate the same sequence of numbers anyway.

And of course you can take as many of the numbers off that list as you wish, with take n.

这篇关于如何在Haskell中链接二进制函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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