我试图在一些数据结构中存储随机生成的骰子值,但不知道在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"
Run Code Online (Sandbox Code Playgroud)
在此之后,我希望能够让用户保持相同的骰子(如积累点)并让他们选择重新掷骰子 - 我认为这可以通过遍历数据结构(具有骰子值)并将重复骰子计数为点并将它们存储在另一个数据结构中.如果用户选择重新滚动,则他必须能够再次调用随机并替换原始数据结构中的值.
我来自OOP背景,Haskell是我的新领域.非常感谢帮助.
所以,有几个问题,让我们一个接一个:
第一:如何使用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)]
Run Code Online (Sandbox Code Playgroud)
不是很好.虽然你可以做得更好,如果你用(键,值)顺序编写对,因为有一点点支持Data.List中的"关联列表"与查找功能:
intToColor n = fromJust . lookup n $ [(1,"Black"),(2,"Green"),(3,"Purple"),(4,"Red"),(5,"White"),(6,"Yellow")]
Run Code Online (Sandbox Code Playgroud)
或者当然,你可以在列表中忘记这个Int键的业务从1到6,因为列表已经被Int索引:
intToColor n = ["Black","Green","Purple","Red","White","Yellow"] !! n
Run Code Online (Sandbox Code Playgroud)
(注意这个函数有点不同,因为intToColor 0现在是"黑色"而不是intToColor 1,但考虑到你的目标,这并不是很重要,如果真的让你震惊,你可以写"!!(n-1)"代替)
但是因为你的颜色不是字符串而且更像是符号,所以你应该创建一个颜色类型:
data Color = Black | Green | Purple | Red | White | Yellow deriving (Eq, Ord, Show, Read, Enum)
Run Code Online (Sandbox Code Playgroud)
所以现在Black是Color类型的值,你可以在你的程序中的任何地方使用它(如果你写Blak,GHC会抗议)并且由于自动推导的魔力,你可以比较Color值,或者显示它们,或者使用toEnum将Int转换为颜色!
所以现在你可以写:
randColorIO :: IO Color
randColorIO = do
n <- randomRIO (0,5)
return (toEnum n)
Run Code Online (Sandbox Code Playgroud)
其次,您希望在数据结构中存储骰子值(颜色),并提供保持相同抛出的选项.所以首先应该存储几次抛出的结果,给定最大同时抛出次数(5)和数据的复杂性,一个简单的列表很多,并且给出了在Haskell中处理列表的函数数量,这是一个不错的选择.
所以你想抛出几个骰子:
nThrows :: Int -> IO [Color]
nThrows 0 = return []
nThrows n = do
c <- randColorIO
rest <- nThrows (n-1)
return (c : rest)
Run Code Online (Sandbox Code Playgroud)
这是一个很好的第一种方法,这就是你所做的,或多或少,除了你使用if而不是模式匹配,你有一个显式的累加器参数(你要去尾递归吗?),除了严格累加器(Int)之外并不是更好而不是列表).
当然,Haskell提升高阶函数而不是直接递归,所以让我们看看组合器,用Hoogle搜索"Int - > IO a - > IO [a]"给你:
replicateM :: Monad m => Int -> m a -> m [a]
Run Code Online (Sandbox Code Playgroud)
这正是你想要的:
nThrows n = replicateM n randColorIO
Run Code Online (Sandbox Code Playgroud)
(我不确定我是否会将其作为函数编写,因为我发现显式表达式更清晰,几乎一样短)
一旦你得到了抛出的结果,你应该检查哪些是相同的,我建议你看看排序,组,地图和长度来实现这个目标(将结果列表转换成相同结果的列表,而不是最多有效的数据结构,但在这个规模,最合适的选择).然后保持你几次获得的颜色只是使用过滤器的问题.
然后你应该写一些更多的函数来处理交互和评分:
type Score = Int
yahtzee :: IO Score
yahtzeeStep :: Int -> [[Color]] -> IO [[Color]] -- recursive
scoring :: [[Color]] -> Score
Run Code Online (Sandbox Code Playgroud)
因此,我建议保留并传输[[颜色]]以跟踪放在一边的内容.这应该足以满足您的需求.