2012-03-06 2 views
3

Я пытаюсь сохранить случайно генерируемые значения костей в некоторой структуре данных, но не знаю, как именно это сделать в Haskell. Я до сих пор мог генерировать случайные ints, но я хочу иметь возможность сравнивать их с соответствующими значениями цвета и вместо этого сохранять цвета (не могу представить, как будет выглядеть эта функция). Вот код, у меня есть -Сохранение значений в структуре данных 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" 

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

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

+0

Какие учебники вы используете для изучения Haskell? –

ответ

6

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

Первого: Как генерировать что-то другое, чем целые числа с функциями от 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 является «Black» в настоящее время а не intToColor 1, но это не очень важно с учетом вашей цели, если это действительно шокирует вас, вы можете написать «!!(П-1)», а)

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

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

Так что теперь Black является значением типа 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 а -> IO [а]" с Hoogle дает:

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

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

nThrows n = replicateM n randColorIO 

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

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

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

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

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

+0

Это было очень тщательным и терпеливым. Большое спасибо. Должен ли я вызвать replicateM n randColorIO в doYahtzee? Потому что я пробовал nThrows n, похоже, он работает; Я не знаю, потому что он не печатает на экране, и когда я попробовал putStrLn (nThrows 5), он не смог совместить [char] с типом IO [color]. Я ожидал, что он вернет список перечислений (цветов), но, похоже, возвращает символы. – rexbelia

+0

Нет, нет (nThrows n) ​​имеет тип IO [Цвет], [Char] - тип, ожидаемый putStrLn (String и [Char] - синонимы), вы видите, что putStrLn не может отображать действие ввода-вывода. Вы должны выполнить действие, получить результат и преобразовать его в String (с показом), чтобы использовать putStrLn на нем. Это может выглядеть так: 'do {colors <- nThrows 5; цвета печати} ', где цвета будут иметь тип [Цвета], а печать - стандартная функция типа (Показать a) => a -> IO(), print x = putStrLn (показать x). – Jedai

+0

В принципе, при ошибке типа GHC у вас есть два типа: «ожидаемый тип», который является типом, который накладывает контекст на выражение (здесь ошибка типа находилась в выражении «nThrows 5» и в контексте «putStrLn _», хотел, чтобы в этом месте был String (== [Char]) и «фактический тип», который является типом выражения, не учитывая контекст (здесь «nThrows 5» имеет тип IO [Цвет]). Ошибка типа дает вам и то и другое, и важно понять, что именно должно решить несоответствие. – Jedai

1

на первый взгляд:

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

хотя haskellers может удалить скобки

+0

'mapM id' является' sequence', но все это можно записать как 'replicateM n $ randomRIO (1, 6)'. Аннотации типа не нужны, поскольку они могут быть выведены из сигнатуры типа. – hammar

+0

Я так и думал, ну, я включил аннотацию типа в качестве заявления о намерениях. –

+0

GHCi бросает «Не может соответствовать ожидаемому типу»() «с фактическим типом» [int] ', когда я вызываю rand 5 в инструкции «then do». Также можно распечатать, чтобы просмотреть содержимое mapM или replicateM as Хаммар предлагает? – rexbelia

3

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

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

Отказ от ответственности: Я тоже очень новичок в Haskell.

+0

Где именно я могу назвать getColor? Я назвал его «then do» doYahtzee как getColor (rand 5 []), но он говорит, что не может соответствовать ожидаемому типу IO() с типом [char]. Что касается правил Yahtzee Jr., пользователь сначала получает бросок 5 кубиков, а затем получает решение с цветами, которые нужно сохранить. S/he идет на 5 поворотов с 3 рулонами каждый (может изменить цвет выбора в пределах 3 рулонов). Невозможно повторить цвета для поворотов и в конце 5 поворотов игра суммирует точки для всех цветов. – rexbelia

+0

Функция 'getColor' будет работать с внутренними элементами вашего списка, а не с вашим списком, как в' map getColor $ rand 5 [] ', что превратит ваш список' Int' в список 'String' , И, кстати, если повороты также имеют значение, они также должны быть параметром для вашей функции playgame. –

+1

Обратите внимание, что ваш 'getColor' эквивалентен' lookup n (map swap diceColor) '. –

Смежные вопросы