简体   繁体   English

Haskell Random无法构造无限类型:a1 = IO a1

[英]Haskell Random cannot construct the infinite type: a1 = IO a1

I want to generate a list with 26 random integers which sum is 301 with Haskell. 我想生成一个包含26个随机整数的列表,其与Haskell的总和为301。 I write the following: 我写了以下内容:

import System.Random

f 1 sum = [sum]
f n sum = m : (f (n-1) (sum-m))
    where m = randomRIO (0,sum)

but it can't be compiled! 但它无法编译! I am confused with IO! 我对IO很困惑!

Occurs check: cannot construct the infinite type: a1 = IO a1
In the first argument of `(:)', namely `m'
In the expression: m : (f (n - 1) (sum - m))
In an equation for `f':
    f n sum
      = m : (f (n - 1) (sum - m))
      where
          m = randomRIO (0, sum)

The error message is somewhat confusing in this case, but the punchline is that you need to work in the IO monad, since it's using randomRIO which is in IO , and there is (by design) no way to run IO code from non- IO code. 在这种情况下,错误消息有点令人困惑,但是你需要在IO monad中工作,因为它使用IO randomRIO ,并且(设计上)没有办法从非IO运行IO代码码。

f 1 sum = return [sum]
f n sum = do
  x  <- randomRIO (0, sum)
  xs <- f (n - 1) (sum - x)
  return (x : xs)

As an aside to what hammer wrote, the error message becomes a lot more clear if you write the type you expect for the f function: 除了锤子所写的内容之外,如果你编写f函数所期望的类型,错误信息会变得更加清晰:

f :: Int -> Int -> [Int]
f 1 sum = [sum]
f n sum = m : (f (n-1) (sum-m))
    where m = randomRIO (0,sum)             

gives the error: 给出错误:

Couldn't match expected type `Int' with actual type `IO Int'
    In the first argument of `(:)', namely `m'
    In the expression: m : (f (n - 1) (sum - m))
    In an equation for `f':
        f n sum
          = m : (f (n - 1) (sum - m))
          where
              m = randomRIO (0, sum)
Failed, modules loaded: none.

Which pretty much tells you exactly what is wrong - that is m has type IO Int rather than Int 这几乎可以告诉你到底出了什么问题 - 这就是m类型IO Int而不是Int

As others have pointed out, your algorithm will not give uniformly-distributed output. 正如其他人指出的那样,你的算法不会给出均匀分布的输出。

An easy way to get uniform output is: 获得统一输出的简单方法是:

  • Generate n-1 random numbers in the range from 0 to sum (inclusive) 产生n-1从范围的随机数0sum (含)
  • Insert 0 and sum into the list of random numbers 0sum插入随机数列表中
  • Sort the resulting list 对结果列表进行排序
  • Return the list of differences between consecutive values in the sorted list 返回排序列表中连续值之间的差异列表

Example: 例:

  • Suppose we want four integers with a sum of 100, we request three random values from the RNG and it gives us [72,33,43] 假设我们想要四个总和为100的整数,我们从RNG请求三个随机值,它给了我们[72,33,43]
  • We insert 0 and 100 and sort the list, giving [0,33,43,72,100] 我们插入0100并对列表进行排序,给出[0,33,43,72,100]
  • We compute the differences [33-0, 43-33, 72-43, 100-72] 我们计算差异[33-0, 43-33, 72-43, 100-72]
  • The result would be [33,10,29,28] 结果将是[33,10,29,28]

In Haskell: 在Haskell:

randomsWithSum :: (Num n, Ord n, Random n) => Int -> n -> IO [n]
randomsWithSum len sum =
    do b <- sequence $ take (len-1) $ repeat $ randomRIO (0,sum)
       let sb = sort (sum:b) in
           return $ zipWith (-) sb (0:sb)

For your example you would call this as randomsWithSum 26 (301::Int) 对于您的示例,您将其称为randomsWithSum 26 (301::Int)

The same applies to floating-point types, eg randomsWithSum 4 (1::Double) 这同样适用于浮点类型,例如randomsWithSum 4 (1::Double)


Edit Swapped the arguments, so that 26 `randomsWithSum` 301 does what its name suggests. 编辑交换参数,以便26 `randomsWithSum` 301其名称建议。

Following the comment by demas, I tried to tweak your algorithm. 根据demas的评论,我试图调整你的算法。 We probably want each of the n numbers be "the same" as all the others, so we'll just try until we get the correct sum. 我们可能希望n数字中的每一个都与其他数字“相同”,所以我们只会尝试直到得到正确的总和。 Maybe there is a better way. 也许有更好的方法。

-- f 0 rng = return []
-- f n rng = randomRIO (0,rng) >>= (\x-> fmap (x:) $ f (n-1) rng)

g n sumval = 
  let s = 2*sumval `div` n  -- expected value upto z is probably z/2,
      h i = do              --              if all are equally likely
              xs <- sequence $ replicate n (randomRIO (0,s))
              if sum xs == sumval 
                then return (xs, i)       -- i is number of attempts
                else h (i+1)
  in h 1

-- test:
Prelude System.Random> g 26 301
([15,23,15,0,13,8,23,11,13,19,5,2,10,19,4,8,3,9,19,16,8,16,18,4,20,0],2)
Prelude System.Random> g 26 301
([20,14,3,6,15,21,7,9,2,23,22,13,2,0,22,9,4,1,15,10,20,7,18,1,18,19],12)
Prelude System.Random> g 26 301
([4,3,4,14,10,16,20,11,19,15,23,18,10,18,12,7,3,8,4,9,11,5,17,4,20,16],44)
Prelude System.Random> g 26 301
([6,6,22,1,5,14,15,21,12,2,4,20,4,9,9,9,23,10,17,19,22,0,10,14,6,21],34)
Prelude System.Random> g 26 301
([20,9,3,1,17,22,10,14,16,16,18,13,15,7,6,3,2,23,13,13,17,18,2,2,8,13],169)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM