如何修复我的 haskell 代码来计算剪刀石头布的第一人称得分?

How can I fix my haskell code to count the first person points in rock paper scissors?

我想计算第一位玩家赢得游戏的次数。有人可以帮我解决吗?

data RPS = Rock | Paper | Scissors deriving (Eq) 

beats :: RPS -> RPS
beats rps = case rps of 
    Rock -> Scissors
    Paper -> Rock
    Scissors -> Paper


firstBeats :: [RPS] -> [RPS] -> Int
firstBeats (a:ax) (b:bx)  
 | a==Rock && b==Scissors = 1 + (firstBeats ax) : (firstBeats bx)
 | a==Scissors && b==Paper = 1 + (firstBeats ax) : (firstBeats bx)
 | a==Paper && b==Rock = 1+ (firstBeats ax) : (firstBeats bx)
 | otherwise = (a: firstBeats ax) + (b : firstBeats bx)

Examples:
firstBeats [Rock] [Paper]    == 0
firstBeats [Rock] [Scissors] == 1
firstBeats [Paper, Scissors] [Rock, Paper] == 2
firstBeats [Paper, Scissors, Paper] [Rock, Paper, Scissors] == 2
firstBeats (replicate 20 Paper) (replicate 20 Rock) == 20

主要问题是firstBeats有两个参数,所以你应该传递两个列表的尾部。此外,您应该涵盖两个列表之一或两个列表都为空的情况,因此:

firstBeats :: [RPS] -> [RPS] -> Int
firstBeats (a:ax) (b:bx)  
 | a == Rock && b == Scissors = 1 + firstBeats ax bx
 | a == Scissors && b == Paper = 1 + firstBeats ax bx
 | a == Paper && b == Rock = 1 + firstBeats ax bx
 | otherwise = firstBeats ax bx
firstBeats _ _ = 0

但这并不优雅,需要大量重复。您可以定义一个函数来检查第一项是否击败了第二项:

hasBeaten :: RPS -> RPS -> Bool
hasBeaten Rock Scissors = True
hasBeaten Scissors Paper = True
hasBeaten Paper Rock = True
hasBeaten _ _ = False

那么你可以将其实现为:

firstBeats :: [RPS] -> [RPS] -> Int
firstBeats (a:as) (b:bs)
    | hasBeaten a b = 1 + firstBeats ax bx
    | otherwise = firstBeats ax bx
firstBeats _ _ = 0

不过造型也不是很好看。人们会期望使用 RPS 的二元组列表,这样就不可能传递两个包含不同数量项目的列表。那么就是:

firstBeats :: [(RPS, RPS)] -> Int
firstBeats = length . filter (uncurry hasBeaten)