我正在尝试在一些数据结构中存储随机生成的骰子值,但不知道在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"
在此之后,我希望能够让用户保持相同的骰子(如积累点)并让他们选择重新掷骰子 - 我认为这可以做到通过遍历数据结构(使用骰子值)并将重复的骰子计数为点并将它们存储在另一个数据结构中。如果用户选择重新滚动,则他必须能够再次调用随机并替换原始数据结构中的值。
我来自OOP背景,Haskell是我的新领域。非常感谢帮助。
答案 0 :(得分: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)]
不是很好。虽然你可以用(key,value)顺序编写对,但是因为在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)
所以现在Black是Color类型的值,你可以在你的程序中的任何地方使用它(如果你写Blak会GHC会抗议)并且由于自动推导的魔力,你可以比较Color值,或者显示它们,或使用toEnum将Int转换为Color!
现在你可以写:
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)
这是一个很好的第一种方法,这就是你所做的,或多或少,除了你使用if而不是模式匹配并且你有一个显式的累加器参数(你要去尾递归?),除了严格以外并不是更好累加器(Int而不是列表)。
当然,Haskell提升高阶函数而不是直接递归,所以让我们看看组合器,用Hoogle搜索“Int - &gt; IO a - &gt; IO [a]”给你:
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
所以我建议保留并发送[[颜色]]以跟踪放在一边的东西。这应该足以满足您的需求。
答案 1 :(得分:3)
你基本上在这里问两个不同的问题。可以使用getColor n = fst . head $ filter (\x -> snd x == n) diceColor
等函数回答第一个问题。
然而,你的第二个问题更有趣。您无法替换元素。你需要一个可以递归调用自己的函数,这个函数将驱动你的游戏。它需要接受当前得分和保留骰子列表作为参数。在进入时,分数将为零,并且保持的骰子列表将为空。然后它将根据需要滚动尽可能多的骰子来填充列表(我不熟悉Yahtzee的规则),将其输出给用户,并要求选择。如果用户选择结束游戏,则该函数返回分数。如果他选择保留一些骰子,该功能会使用当前分数和保留骰子列表来调用自己。所以,总结一下,playGame :: Score -> [Dice] -> IO Score
。
免责声明:我也是Haskell的初学者。
答案 2 :(得分:1)
起初想:
rand :: Int -> IO [Int]
rand n = mapM id (take n (repeat (randomRIO (1::Int, 6))))
虽然haskellers可以删除parens