Хранение значений в структуре данных Haskell - PullRequest
3 голосов
/ 06 марта 2012

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

module Main where

import System.IO
import System.Random
import Data.List

diceColor = [("Black",1),("Green",2),("Purple",3),("Red",4),("White",5),("Yellow",6)]
diceRoll = []

rand :: Int -> [Int] -> IO ()
rand n rlst = do
       num <- randomRIO (1::Int, 6)
       if n == 0
        then printList rlst       -- here is where I need to do something to store the values
        else rand (n-1) (num:rlst)

printList x = putStrLn (show (sort x))

--matchColor x = doSomething()

main :: IO ()
main = do
    --hSetBuffering stdin LineBuffering
    putStrLn "roll, keep, score?"
    cmd <- getLine
    doYahtzee cmd
    --rand (read cmd) []

doYahtzee :: String -> IO ()
doYahtzee cmd = do
if cmd == "roll" 
    then do rand 5 []
        else putStrLn "Whatever"

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

Я родом из ООП, и Хаскелл для меня - новая территория. Помощь очень ценится.

Ответы [ 3 ]

6 голосов
/ 07 марта 2012

Итак, несколько вопросов, давайте ответим на них один за другим:

Первый : Как сгенерировать что-то еще, кроме целых чисел, с помощью функций из System.Random (медленный генератор,но для вашего приложения производительность не является жизненно важной).Есть несколько подходов, с вашим списком, вам придется написать функцию intToColor:

intToColor :: Int -> String
intToColor n = head . filter (\p -> snd p == n) $ [("Black",1),("Green",2),("Purple",3),("Red",4),("White",5),("Yellow",6)]

Не очень приятно.Хотя вы могли бы добиться большего, если бы записали пару в порядке (ключ, значение), поскольку в Data.List есть небольшая поддержка «списка ассоциаций» с функцией поиска:

intToColor n = fromJust . lookup n $ [(1,"Black"),(2,"Green"),(3,"Purple"),(4,"Red"),(5,"White"),(6,"Yellow")]

Иликонечно, вы могли бы просто забыть об этом ключе Int от 1 до 6 в списке, поскольку списки уже проиндексированы по Int:

intToColor n = ["Black","Green","Purple","Red","White","Yellow"] !! n

(обратите внимание, что эта функция немного отличаетсятак как intToColor 0 теперь «черный», а не intToColor 1, но это не очень важно, учитывая вашу цель, если вас действительно шокирует, вы можете написать «!! (n-1)» вместо)

Нопоскольку ваши цвета на самом деле не являются строками и больше похожи на символы, вам, вероятно, следует создать тип цвета:

data Color = Black | Green | Purple | Red | White | Yellow deriving (Eq, Ord, Show, Read, Enum)

Так что теперь черный - это значение типа Color, вы можете использовать его в любом месте вашей программы (и GHCбудет протестовать, если вы напишите Blak), и благодаря магии автоматического вывода вы можете сравнить значения цвета, или показать их, или использовать toEnum, чтобы преобразовать Int в цвет!

Так что теперь вы можетеНапишите:

randColorIO :: IO Color
randColorIO = do
   n <- randomRIO (0,5)
   return (toEnum n)

Секунда , вы хотите сохранить значения (цвета) игральных костей в структуре данных и дать возможность сохранять идентичные броски.Итак, сначала вы должны сохранить результаты нескольких бросков, учитывая максимальное количество одновременных бросков (5) и сложность ваших данных, простого списка достаточно, и учитывая количество функций для обработки списков в Haskell, это хороший выбор..

Итак, вы хотите бросить несколько кубиков:

nThrows :: Int -> IO [Color]
nThrows 0 = return []
nThrows n = do
   c <- randColorIO
   rest <- nThrows (n-1)
   return (c : rest)

Это хороший первый подход, это то, что вы делаете, более или менее, за исключением того, что вы используете вместо сопоставления с образцом и у вас естьявный аргумент аккумулятора (вы собирались использовать хвостовую рекурсию?), не лучше, если бы не строгий аккумулятор (Int, а не списки).

Конечно, Haskell продвигает функции высшего порядка, а не прямую рекурсию, поэтомудавайте посмотрим на комбинаторы, которые ищут в «Int -> IO a -> IO [a]» с помощью Google:

replicateM :: Monad m => Int -> m a -> m [a]

, который делает именно то, что вы хотите:

nThrows n = replicateM n randColorIO

(Я не уверен, что я бы даже написал это как функцию, поскольку я нахожу явное выражение более ясным и почти таким же коротким)

Как только вы получите результатыиз бросков, вы должны проверить, какие идентичные, я предлагаю вам взглянуть на сортировку, группу, карту и длину для достижения этой цели (преобразование вашего списка результатов в список из списка идентичных результатов, не самая эффективная из структуры данных, нов этом масштабе наиболее подходящий выбор).Тогда сохранение цветов, которые вы получили несколько раз, - это всего лишь вопрос использования фильтра.

Затем вы должны написать еще несколько функций для управления взаимодействием и оценкой:

type Score = Int
yahtzee :: IO Score
yahtzeeStep :: Int -> [[Color]] -> IO [[Color]] -- recursive
scoring :: [[Color]] -> Score

Поэтому я рекомендую сохранить ипередать [[Цвет]], чтобы отслеживать то, что было отложено.Этого должно быть достаточно для ваших нужд.

3 голосов
/ 06 марта 2012

Вы в основном задаете два разных вопроса здесь. На первый вопрос можно ответить с помощью функции, подобной getColor n = fst . head $ filter (\x -> snd x == n) diceColor.

Ваш второй вопрос, однако, гораздо интереснее. Вы не можете заменить элементы. Вам нужна функция, которая может вызывать себя рекурсивно, и эта функция будет управлять вашей игрой. Он должен принять в качестве параметров текущий счет и список сохраненных костей. На входе счет будет нулевым, а список сохраненных костей будет пустым. Затем он будет бросать столько кубиков, сколько необходимо для заполнения списка (я не знаком с правилами Yahtzee), выводить его пользователю и запрашивать выбор. Если пользователь решает завершить игру, функция возвращает счет. Если он решает оставить несколько кубиков, функция вызывает себя с текущим счетом и списком сохраненных кубиков. Итак, подведем итог: playGame :: Score -> [Dice] -> IO Score.

Отказ от ответственности: Я тоже очень начинающий в Хаскеле.

1 голос
/ 06 марта 2012

сначала подумал:

rand :: Int -> IO [Int]
rand n = mapM id (take n (repeat (randomRIO (1::Int, 6))))

хотя хакеллер мог убрать паренов

...