使用 Haskell 的随机元素网格
Random grid of elements using Haskell
对 Haskell 的经验绝对为零,我需要想出一个与此 Python 等效的代码:
from random import choice, sample
def random_subset():
return tuple(sample(('N', 'S', 'W', 'E'), choice((1, 2, 3, 4))))
def form_grid(n):
return [[random_subset() for _ in range(n)] for _ in range(n)]
form_grid(10)
产生这样的东西:
N ESWN SNEW NSE EWSN E ENSW N NSWE WES
NE WNS SWEN EWN ENWS WEN WS W ENSW NW
WENS NWE SNEW ES E S ES SENW EW WEN
NSE NE WNE NEWS SNE W SWNE NSWE SNEW EN
S SNW WNES S WESN E ES N ESN ES
SWEN S WSNE NEWS WESN E S SE E N
NEW S NEW WS W EN N NWS E WENS
WN NWE S SEW NESW EWSN WENS ES NWS WN
W NWE N N ES E E WN SWNE NES
WENS NWE NW WESN SW NES ENWS SE N SWNE
看在上帝的份上,我无法理解 Haskell 的 IO 概念(尤其是随机性)。我能想到的最好的是:
import Data.Random hiding (shuffle, sample)
import Data.Random.Source.Std
import Data.Random.Extras
randSubset :: IO [Char]
randSubset = do
len <- runRVar (choice [1..4]) StdRandom :: IO Int
subset <- runRVar (sample len ['N', 'S', 'W', 'E']) StdRandom :: IO [Char]
return subset
formGrid :: Int -> [[IO [Char]]]
formGrid n = [[subset | _ <- [0..(n - 1)], subset <- randSubset] | _ <- [0..(n - 1)]]
还是没做到:
error:
* Couldn't match expected type `[IO [Char]]'
with actual type `IO [Char]'
* In the expression: randSubset
In a stmt of a list comprehension: subset <- randSubset
In the expression:
[subset | _ <- [0 .. (n - 1)], subset <- randSubset]
|
12 | formGrid n = [[subset | _ <- [0..(n - 1)], subset <- randSubset] | _ <- [0..(n - 1)]]
| ^^^^^^^^^^
快速谷歌搜索没有太大帮助 - 我可能没有使用最准确的关键字来解决我面临的这个问题。随机更改并希望最好的结果变得非常令人沮丧,但我真的没有时间,也没有精力去正确地研究 Haskell(即使这是一种耻辱),所以现在,我希望有人能告诉我这段代码有什么问题。
如错误所述,您的列表理解有一个生成器:
formGrid n = [[subset | _ <- [0..(n - 1)], <b>subset <- randSubset</b>] | _ <- [0..(n - 1)]]
所以这意味着它期望 randSubset
是某物的列表,但它不是某物的列表,它是某物的列表的 IO
。因此你不能使用它。
你这个函数的类型也有点问题,你用的是[[IO [Char]]]
,所以一个IO [Char]
的矩阵。
您可能正在寻找 replicateM :: Monad m => Int -> m a -> m [a]
,因此您的程序如下所示:
import Control.Monad(<b>replicateM</b>)
formGrid :: Int -> <b>IO [[[Char]]]</b>
formGrid n = <B>replicateM n</b> (<b>replicateM n</b> randSubset)
例如:
Main> formGrid 3
[["WSNE","WNS","S"],["WN","SN","WEN"],["SEWN","ESN","NESW"]]
为了补充 Willem 的回答,我要补充一点,您的 randSubset
看起来相当复杂。这是一个更简单的选择
randSubset :: IO String
randSubset = do
n <- sample (Uniform 1 4) -- choose how many elements
sample (shuffleNofM n 4 "NSWE") -- take that many elements
(顺便说一句,那是 Data.Random
的 sample
)
您应该检查这确实是子集的预期分布。请注意,这不是均匀分布:N
比 NS
更有可能(甚至 NS
和 SN
组合)。另请注意,同一子集的每个排列都可能发生,因此我们并不是真正的采样 "subsets"。我不知道您的 Python 代码使用的是哪个发行版——毕竟可能是一样的。
如果您在 IO
内部工作,我认为如果您使用 sample (someDistribution)
比在 RVar
的较低级别工作更简单。
在此之后,您可以使用 replicateM
生成网格,如 Willem 所示。
您试图一次跳得太远。从小处着手,
formRow :: Int -> IO [[Char]]
formRow 0 = return []
formRow n = do {
subset <- randSubset ; -- subset :: [Char] <- randSubset :: IO [Char]
subsets <- formRow (n-1) ; -- ~~~~~~ ~~~~~~
return (subset : subsets) -- IO [[Char]]
}
...只需使用您已有的,在这里。然后对行执行相同的操作,
formGrid :: Int -> IO [[[Char]]]
formGrid 0 = return []
formGrid n = do {
row <- formRow n ; -- row :: [[Char]] <- formRow n :: IO [[Char]]
rows <- formGrid (n-1) ; -- ~~~~~~~~ ~~~~~~~~
return (row : rows) -- IO [[[Char]]]
}
大功告成。不要害怕 do
符号,它是你的朋友。它很容易编程,因为这是它的全部目的。
当你编写下一个单子组合器时,检查它的类型 on Hoogle 看看它是否已经在库中。
确实,抽象,我们看到我们已经重新实现了
replicateM :: Monad m => Int -> m a -> m [a]
formRow n = replicateM n randSubset
formGrid n = replicateM n (formRow n)
= replicateM n (replicateM n randSubset)
(正如 Willem 的回答所说;但现在我们知道 如何 和 为什么)。
对 Haskell 的经验绝对为零,我需要想出一个与此 Python 等效的代码:
from random import choice, sample
def random_subset():
return tuple(sample(('N', 'S', 'W', 'E'), choice((1, 2, 3, 4))))
def form_grid(n):
return [[random_subset() for _ in range(n)] for _ in range(n)]
form_grid(10)
产生这样的东西:
N ESWN SNEW NSE EWSN E ENSW N NSWE WES
NE WNS SWEN EWN ENWS WEN WS W ENSW NW
WENS NWE SNEW ES E S ES SENW EW WEN
NSE NE WNE NEWS SNE W SWNE NSWE SNEW EN
S SNW WNES S WESN E ES N ESN ES
SWEN S WSNE NEWS WESN E S SE E N
NEW S NEW WS W EN N NWS E WENS
WN NWE S SEW NESW EWSN WENS ES NWS WN
W NWE N N ES E E WN SWNE NES
WENS NWE NW WESN SW NES ENWS SE N SWNE
看在上帝的份上,我无法理解 Haskell 的 IO 概念(尤其是随机性)。我能想到的最好的是:
import Data.Random hiding (shuffle, sample)
import Data.Random.Source.Std
import Data.Random.Extras
randSubset :: IO [Char]
randSubset = do
len <- runRVar (choice [1..4]) StdRandom :: IO Int
subset <- runRVar (sample len ['N', 'S', 'W', 'E']) StdRandom :: IO [Char]
return subset
formGrid :: Int -> [[IO [Char]]]
formGrid n = [[subset | _ <- [0..(n - 1)], subset <- randSubset] | _ <- [0..(n - 1)]]
还是没做到:
error:
* Couldn't match expected type `[IO [Char]]'
with actual type `IO [Char]'
* In the expression: randSubset
In a stmt of a list comprehension: subset <- randSubset
In the expression:
[subset | _ <- [0 .. (n - 1)], subset <- randSubset]
|
12 | formGrid n = [[subset | _ <- [0..(n - 1)], subset <- randSubset] | _ <- [0..(n - 1)]]
| ^^^^^^^^^^
快速谷歌搜索没有太大帮助 - 我可能没有使用最准确的关键字来解决我面临的这个问题。随机更改并希望最好的结果变得非常令人沮丧,但我真的没有时间,也没有精力去正确地研究 Haskell(即使这是一种耻辱),所以现在,我希望有人能告诉我这段代码有什么问题。
如错误所述,您的列表理解有一个生成器:
formGrid n = [[subset | _ <- [0..(n - 1)], <b>subset <- randSubset</b>] | _ <- [0..(n - 1)]]
所以这意味着它期望 randSubset
是某物的列表,但它不是某物的列表,它是某物的列表的 IO
。因此你不能使用它。
你这个函数的类型也有点问题,你用的是[[IO [Char]]]
,所以一个IO [Char]
的矩阵。
您可能正在寻找 replicateM :: Monad m => Int -> m a -> m [a]
,因此您的程序如下所示:
import Control.Monad(<b>replicateM</b>)
formGrid :: Int -> <b>IO [[[Char]]]</b>
formGrid n = <B>replicateM n</b> (<b>replicateM n</b> randSubset)
例如:
Main> formGrid 3
[["WSNE","WNS","S"],["WN","SN","WEN"],["SEWN","ESN","NESW"]]
为了补充 Willem 的回答,我要补充一点,您的 randSubset
看起来相当复杂。这是一个更简单的选择
randSubset :: IO String
randSubset = do
n <- sample (Uniform 1 4) -- choose how many elements
sample (shuffleNofM n 4 "NSWE") -- take that many elements
(顺便说一句,那是 Data.Random
的 sample
)
您应该检查这确实是子集的预期分布。请注意,这不是均匀分布:N
比 NS
更有可能(甚至 NS
和 SN
组合)。另请注意,同一子集的每个排列都可能发生,因此我们并不是真正的采样 "subsets"。我不知道您的 Python 代码使用的是哪个发行版——毕竟可能是一样的。
如果您在 IO
内部工作,我认为如果您使用 sample (someDistribution)
比在 RVar
的较低级别工作更简单。
在此之后,您可以使用 replicateM
生成网格,如 Willem 所示。
您试图一次跳得太远。从小处着手,
formRow :: Int -> IO [[Char]]
formRow 0 = return []
formRow n = do {
subset <- randSubset ; -- subset :: [Char] <- randSubset :: IO [Char]
subsets <- formRow (n-1) ; -- ~~~~~~ ~~~~~~
return (subset : subsets) -- IO [[Char]]
}
...只需使用您已有的,在这里。然后对行执行相同的操作,
formGrid :: Int -> IO [[[Char]]]
formGrid 0 = return []
formGrid n = do {
row <- formRow n ; -- row :: [[Char]] <- formRow n :: IO [[Char]]
rows <- formGrid (n-1) ; -- ~~~~~~~~ ~~~~~~~~
return (row : rows) -- IO [[[Char]]]
}
大功告成。不要害怕 do
符号,它是你的朋友。它很容易编程,因为这是它的全部目的。
当你编写下一个单子组合器时,检查它的类型 on Hoogle 看看它是否已经在库中。
确实,抽象,我们看到我们已经重新实现了
replicateM :: Monad m => Int -> m a -> m [a]
formRow n = replicateM n randSubset
formGrid n = replicateM n (formRow n)
= replicateM n (replicateM n randSubset)
(正如 Willem 的回答所说;但现在我们知道 如何 和 为什么)。