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

spe*_*ing 4 random io haskell

我想生成一个包含26个随机整数的列表,其与Haskell的总和为301.我写了以下内容:

import System.Random

f 1 sum = [sum]
f n sum = m : (f (n-1) (sum-m))
    where m = randomRIO (0,sum)
Run Code Online (Sandbox Code Playgroud)

但它无法编译!我对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)
Run Code Online (Sandbox Code Playgroud)

ham*_*mar 8

在这种情况下,错误消息有点令人困惑,但最重要的是你需要在IOmonad中工作,因为它正在使用randomRIOIO,并且(按设计)没有办法IO从非IO代码运行代码.

f 1 sum = return [sum]
f n sum = do
  x  <- randomRIO (0, sum)
  xs <- f (n - 1) (sum - x)
  return (x : xs)
Run Code Online (Sandbox Code Playgroud)


Dav*_*ani 5

除了锤子所写的内容之外,如果您编写f函数所期望的类型,则错误消息会变得更加清晰:

f :: Int -> Int -> [Int]
f 1 sum = [sum]
f n sum = m : (f (n-1) (sum-m))
    where m = randomRIO (0,sum)             
Run Code Online (Sandbox Code Playgroud)

给出错误:

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.
Run Code Online (Sandbox Code Playgroud)

这几乎可以告诉你到底出了什么问题 - 这是m有类型IO Int而不是Int


fin*_*nnw 5

正如其他人指出的那样,你的算法不会给出均匀分布的输出.

获得统一输出的简单方法是:

  • 生成n-1从(0sum)的范围内的随机数
  • 插入0sum成随机数的列表
  • 对结果列表进行排序
  • 返回排序列表中连续值之间的差异列表

例:

  • 假设我们想要四个总和为100的整数,我们从RNG请求三个随机值,它给了我们 [72,33,43]
  • 我们插入0并对100列表进行排序,给出[0,33,43,72,100]
  • 我们计算差异 [33-0, 43-33, 72-43, 100-72]
  • 结果将是 [33,10,29,28]

在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)
Run Code Online (Sandbox Code Playgroud)

对于你的例子,你会称之为 randomsWithSum 26 (301::Int)

这同样适用于浮点类型,例如 randomsWithSum 4 (1::Double)


编辑交换参数,这样26 `randomsWithSum` 301做就是它的名字所暗示的.