无法将类型“IO”与“[]”匹配
Couldn't match type ‘IO’ with ‘[]’
我编写了一个函数来生成两个随机数,然后将其传递给另一个函数以在那里使用它们。代码是:
randomIntInRange :: (Int, Int, Int, Int) -> Board
randomIntInRange (min,max,min2,max2) = do r <- randomRIO (min, max)
r2 <- randomRIO (min2, max2)
randomCherryPosition (r, r2)
此函数在其 'do' 块中调用的函数是:
randomCherryPosition :: (Int, Int) -> Board
randomCherryPosition (x, y) = initialBoard & element y . element x .~ C
其中initialBoard
是列表的列表,C是预定义的数据类型。我正在使用 lens
来更改列表中的值。 运行 这给了我错误:
Couldn't match type ‘IO’ with ‘[]’
Expected type: [Int]
Actual type: IO Int
对于 r 和 r2 行。我完全不知道这里发生了什么,或者我做错了什么,所以我将不胜感激任何帮助。
randomRIO
的类型为 IO Int
,而不是 Int
。只要你使用任何 IO
函数,你周围的函数也必须在 IO
:
randomIntInRange :: (Int, Int, Int, Int) -> IO Board
randomIntInRange (min,max,min2,max2) = do r <- randomRIO (min, max)
r2 <- randomRIO (min2, max2)
pure $ randomCherryPosition (r, r2)
randomRIO
不是纯函数。它 returns 每次都是不同的值。 Haskell 禁止此类功能。禁止此类功能有很多好处,我将在此处进行介绍。但是你可以仍然有这样的功能,如果你把它包装在IO
中。类型 IO Int
表示“它是一个程序,在执行时将产生一个 Int
”。因此,当您调用 randomRIO (min, max)
时,它 returns 您不是一个 Int
,而是一个程序,您可以执行该程序以获得 Int
。您通过带有向左箭头的 do
符号来执行,但其结果也将是一个类似的程序。
不幸的是,这个问题没有完美的解决方案。它已经在 Whosebug 上讨论过,例如 。
Fyodor提供的上述解决方案涉及到IO。有用。主要缺点是 IO 会传播到您的类型系统中。
但是,并不一定要因为要使用随机数就涉及到IO。可以深入讨论所涉及的利弊 there。
没有完美的解决方案,因为每次选择时 something 必须负责更新随机数生成器的 state一个随机值。在 C/C++/Fortran 等命令式语言中,我们为此使用 side effects。但是 Haskell 函数没有副作用。这样 something 可以是:
- Haskell IO 子系统(如
randomRIO
)
- 你自己作为程序员 - 请参阅下面的代码示例 #1
- 一个更专业的 Haskell 子系统,为此您需要:
import Control.Monad.Random
- 请参阅下面的代码示例 #2
您可以通过使用库函数 mkStdGen
创建您自己的随机数生成器,然后围绕您的计算手动传递此生成器的更新状态,从而在不涉及 IO 的情况下解决问题。在你的问题中,这给出了这样的东西:
-- code sample #1
import System.Random
-- just for type check:
data Board = Board [(Int, Int)] deriving Show
initialBoard :: Board
initialBoard = Board [(0, 0)]
randomCherryPosition :: (Int, Int) -> Board
randomCherryPosition (x, y) = -- just for type check
let ls0 = (\(Board ls) -> ls) initialBoard
ls1 = (x, y) : ls0
in Board ls1
-- initial version with IO:
randomIntInRange :: (Int, Int, Int, Int) -> IO Board
randomIntInRange (min,max, min2,max2) = do r1 <- randomRIO (min, max)
r2 <- randomRIO (min2, max2)
return $ randomCherryPosition (r1, r2)
-- version with manual passing of state:
randomIntInRangeA :: RandomGen tg => (Int, Int, Int, Int) -> tg -> (Board, tg)
randomIntInRangeA (min1,max1, min2,max2) rng0 =
let (r1, rng1) = randomR (min1, max1) rng0
(r2, rng2) = randomR (min2, max2) rng1 -- pass the newer RNG
board = randomCherryPosition (r1, r2)
in (board, rng2)
main = do
-- get a random number generator:
let mySeed = 54321 -- actually better to pass seed from the command line.
let rng0 = mkStdGen mySeed
let (board1, rng) = randomIntInRangeA (0,10, 0,100) rng0
putStrLn $ show board1
这很麻烦,但可以解决。
一个更优雅的替代方法是使用 MonadRandom。
这个想法是定义一个表示涉及随机性的计算的单子动作,然后使用恰当命名的 runRand
函数 运行 这个动作。
这给出了这个代码:
-- code sample #2
import System.Random
import Control.Monad.Random
-- just for type check:
data Board = Board [(Int, Int)] deriving Show
initialBoard :: Board
initialBoard = Board [(0, 0)]
-- just for type check:
randomCherryPosition :: (Int, Int) -> Board
randomCherryPosition (x, y) =
let ls0 = (\(Board ls) -> ls) initialBoard
ls1 = (x, y) : ls0
in Board ls1
-- monadic version of randomIntInRange:
randomIntInRangeB :: RandomGen tg => (Int, Int, Int, Int) -> Rand tg Board
randomIntInRangeB (min1,max1, min2,max2) =
do
r1 <- getRandomR (min1,max1)
r2 <- getRandomR (min2,max2)
return $ randomCherryPosition (r1, r2)
main = do
-- get a random number generator:
let mySeed = 54321 -- actually better to pass seed from the command line.
let rng0 = mkStdGen mySeed
-- create and run the monadic action:
let action = randomIntInRangeB (0,10, 0,100) -- of type: Rand tg Board
let (board1, rng) = runRand action rng0
putStrLn $ show board1
这绝对比代码示例 #1 更不容易出错,因此一旦您的计算变得足够复杂,您通常会更喜欢此解决方案。所有涉及的函数都是普通的 pure Haskell 函数,编译器可以使用其通常的技术对其进行充分优化。
我编写了一个函数来生成两个随机数,然后将其传递给另一个函数以在那里使用它们。代码是:
randomIntInRange :: (Int, Int, Int, Int) -> Board
randomIntInRange (min,max,min2,max2) = do r <- randomRIO (min, max)
r2 <- randomRIO (min2, max2)
randomCherryPosition (r, r2)
此函数在其 'do' 块中调用的函数是:
randomCherryPosition :: (Int, Int) -> Board
randomCherryPosition (x, y) = initialBoard & element y . element x .~ C
其中initialBoard
是列表的列表,C是预定义的数据类型。我正在使用 lens
来更改列表中的值。 运行 这给了我错误:
Couldn't match type ‘IO’ with ‘[]’
Expected type: [Int]
Actual type: IO Int
对于 r 和 r2 行。我完全不知道这里发生了什么,或者我做错了什么,所以我将不胜感激任何帮助。
randomRIO
的类型为 IO Int
,而不是 Int
。只要你使用任何 IO
函数,你周围的函数也必须在 IO
:
randomIntInRange :: (Int, Int, Int, Int) -> IO Board
randomIntInRange (min,max,min2,max2) = do r <- randomRIO (min, max)
r2 <- randomRIO (min2, max2)
pure $ randomCherryPosition (r, r2)
randomRIO
不是纯函数。它 returns 每次都是不同的值。 Haskell 禁止此类功能。禁止此类功能有很多好处,我将在此处进行介绍。但是你可以仍然有这样的功能,如果你把它包装在IO
中。类型 IO Int
表示“它是一个程序,在执行时将产生一个 Int
”。因此,当您调用 randomRIO (min, max)
时,它 returns 您不是一个 Int
,而是一个程序,您可以执行该程序以获得 Int
。您通过带有向左箭头的 do
符号来执行,但其结果也将是一个类似的程序。
不幸的是,这个问题没有完美的解决方案。它已经在 Whosebug 上讨论过,例如
Fyodor提供的上述解决方案涉及到IO。有用。主要缺点是 IO 会传播到您的类型系统中。
但是,并不一定要因为要使用随机数就涉及到IO。可以深入讨论所涉及的利弊 there。
没有完美的解决方案,因为每次选择时 something 必须负责更新随机数生成器的 state一个随机值。在 C/C++/Fortran 等命令式语言中,我们为此使用 side effects。但是 Haskell 函数没有副作用。这样 something 可以是:
- Haskell IO 子系统(如
randomRIO
) - 你自己作为程序员 - 请参阅下面的代码示例 #1
- 一个更专业的 Haskell 子系统,为此您需要:
import Control.Monad.Random
- 请参阅下面的代码示例 #2
您可以通过使用库函数 mkStdGen
创建您自己的随机数生成器,然后围绕您的计算手动传递此生成器的更新状态,从而在不涉及 IO 的情况下解决问题。在你的问题中,这给出了这样的东西:
-- code sample #1
import System.Random
-- just for type check:
data Board = Board [(Int, Int)] deriving Show
initialBoard :: Board
initialBoard = Board [(0, 0)]
randomCherryPosition :: (Int, Int) -> Board
randomCherryPosition (x, y) = -- just for type check
let ls0 = (\(Board ls) -> ls) initialBoard
ls1 = (x, y) : ls0
in Board ls1
-- initial version with IO:
randomIntInRange :: (Int, Int, Int, Int) -> IO Board
randomIntInRange (min,max, min2,max2) = do r1 <- randomRIO (min, max)
r2 <- randomRIO (min2, max2)
return $ randomCherryPosition (r1, r2)
-- version with manual passing of state:
randomIntInRangeA :: RandomGen tg => (Int, Int, Int, Int) -> tg -> (Board, tg)
randomIntInRangeA (min1,max1, min2,max2) rng0 =
let (r1, rng1) = randomR (min1, max1) rng0
(r2, rng2) = randomR (min2, max2) rng1 -- pass the newer RNG
board = randomCherryPosition (r1, r2)
in (board, rng2)
main = do
-- get a random number generator:
let mySeed = 54321 -- actually better to pass seed from the command line.
let rng0 = mkStdGen mySeed
let (board1, rng) = randomIntInRangeA (0,10, 0,100) rng0
putStrLn $ show board1
这很麻烦,但可以解决。
一个更优雅的替代方法是使用 MonadRandom。
这个想法是定义一个表示涉及随机性的计算的单子动作,然后使用恰当命名的 runRand
函数 运行 这个动作。
这给出了这个代码:
-- code sample #2
import System.Random
import Control.Monad.Random
-- just for type check:
data Board = Board [(Int, Int)] deriving Show
initialBoard :: Board
initialBoard = Board [(0, 0)]
-- just for type check:
randomCherryPosition :: (Int, Int) -> Board
randomCherryPosition (x, y) =
let ls0 = (\(Board ls) -> ls) initialBoard
ls1 = (x, y) : ls0
in Board ls1
-- monadic version of randomIntInRange:
randomIntInRangeB :: RandomGen tg => (Int, Int, Int, Int) -> Rand tg Board
randomIntInRangeB (min1,max1, min2,max2) =
do
r1 <- getRandomR (min1,max1)
r2 <- getRandomR (min2,max2)
return $ randomCherryPosition (r1, r2)
main = do
-- get a random number generator:
let mySeed = 54321 -- actually better to pass seed from the command line.
let rng0 = mkStdGen mySeed
-- create and run the monadic action:
let action = randomIntInRangeB (0,10, 0,100) -- of type: Rand tg Board
let (board1, rng) = runRand action rng0
putStrLn $ show board1
这绝对比代码示例 #1 更不容易出错,因此一旦您的计算变得足够复杂,您通常会更喜欢此解决方案。所有涉及的函数都是普通的 pure Haskell 函数,编译器可以使用其通常的技术对其进行充分优化。