Случайная сетка элементов с использованием Haskell - PullRequest
2 голосов
/ 22 мая 2019

Имея абсолютно нулевой опыт работы с 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 

Я, ради бога, не могу обернуться вокруг концепции 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)]]
   |                                                      ^^^^^^^^^^

Быстрое поиск в Google не сильно помогло - я, вероятно, не использовал самые точные ключевые слова для этой проблемы, с которой я сталкиваюсь. Вносить случайные изменения и надеяться на лучшее становится довольно неприятно, но у меня действительно нет ни времени, ни энергии, чтобы должным образом погрузиться в Haskell (хотя это позор), так что пока я бы хотел, чтобы кто-то просто указал мне на то, что неправильно с этим кодом.

Ответы [ 3 ]

5 голосов
/ 22 мая 2019

Как уже говорилось в ошибке, у вашего понимания списка есть генератор:

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"]]
2 голосов
/ 22 мая 2019

В дополнение к ответу Виллема добавлю, что ваш 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)

Вы должны убедиться, что это действительно предполагаемое распределение подмножеств. Обратите внимание, что это не является равномерным распределением: N более вероятно, чем NS (или даже оба NS и SN вместе взятые). Также обратите внимание, что каждая перестановка одного и того же подмножества может происходить, поэтому мы на самом деле не выбираем «подмножества». Я не знаю, какой дистрибутив используется вашим кодом Python - в конце концов, он может быть таким же.

Если вы работаете внутри IO, я думаю, что проще, если вы используете sample (someDistribution) вместо того, чтобы работать на нижнем уровне RVar с.

После этого вы можете использовать replicateM для генерации вашей сетки, как показал Виллем.

1 голос
/ 02 июня 2019

Вы пытались прыгнуть слишком далеко сразу.Начните с малого,

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)

(так же, как говорит ответ Виллема; но теперь мы знаем как и почему ).

...