2

对 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(尽管这很遗憾),所以现在,我希望有人能指出我的想法这段代码错了。

4

3 回答 3

5

就像错误已经说的那样,您的列表理解有一个生成器:

formGrid n = [[subset | _ <- [0..(n - 1)], subset <- randSubset] | _ <- [0..(n - 1)]]

所以这意味着它期望randSubset是一个东西的列表,但它不是一个东西的列表,它是一个IO东西的列表。因此,您不能使用它。

你的函数的类型也有点问题,你使用[[IO [Char]]],所以是 s 的矩阵IO [Char]

您可能正在寻找replicateM :: Monad m => Int -> m a -> m [a],因此您的程序如下所示:

import Control.Monad(replicateM)

formGrid :: Int -> IO [[[Char]]]
formGrid n = replicateM n (replicateM n randSubset)

例如:

Main> formGrid 3
[["WSNE","WNS","S"],["WN","SN","WEN"],["SEWN","ESN","NESW"]]
于 2019-05-22T11:10:09.663 回答
2

为了补充威廉的回答,我要补充一点,你的randSubset样子相当复杂。这是一个更简单的选择

randSubset :: IO String
randSubset = do
  n <- sample (Uniform 1 4)        -- choose how many elements
  sample (shuffleNofM n 4 "NSWE")  -- take that many elements

(顺便说一句,这是sample来自Data.Random

您应该检查这确实是子集的预期分布。请注意,这不是均匀分布:NNS(或什至两者兼而有之NS)更有可能SN。另请注意,同一子集的每个排列都可能发生,因此我们并不是真正对“子集”进行采样。我不知道你的 Python 代码使用的是哪个发行版——毕竟它可能是一样的。

如果你在内部工作IO,我认为如果你使用sample (someDistribution)而不是在RVars 的较低级别工作会更简单。

在此之后,您可以使用replicateM生成网格,如 Willem 所示。

于 2019-05-22T12:17:44.233 回答
1

你试图一次跳得太远。从小处着手,

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符号,它是你的朋友。它很容易编程,因为这是它的全部目的。

编写下一个单子组合器后,请在 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)

(正如威廉的回答所说;但现在我们知道如何以及为什么)。

于 2019-06-02T16:22:17.097 回答