我正在尝试解决这个问题:
您将获得4种颜色的N球序列:红色,绿色,黄色 和蓝色。当且仅当所有的序列都充满了颜色 以下条件属实:
- 红球和绿球一样多。
- 黄球和蓝球一样多。
- 序列的每个前缀中的红球和绿球数量之差最多为1。
- 序列的每个前缀中黄球和蓝球的数量之差最多为1.
您的任务是编写一个程序,如果某个序列中充满了颜色,则该程序会打印
True
,否则会打印False
。
到目前为止我的解决方案是:
module Main where
import Control.Monad
main = do
s <- readLn :: IO Int
elements <- replicateM s getLine
let result = map checkColours elements
mapM (putStrLn . show) result
checkColours :: String -> Bool
checkColours string = isFullOfColors 0 0 0 0 string
isFullOfColors :: Int -> Int -> Int -> Int -> String -> Bool
isFullOfColors red green yellow blue string
| (abs (red - green)) > 1 = False
| (abs (yellow - blue)) > 1 = False
| (string == []) = if (red /= yellow) || (green /= blue) then True else False
| (head string == 'R' ) = isFullOfColors (red + 1) green yellow blue (tail string)
| (head string == 'G' ) = isFullOfColors red (green + 1) yellow blue (tail string)
| (head string == 'Y' ) = isFullOfColors red green (yellow + 1) blue (tail string)
| (head string == 'B' ) = isFullOfColors red green yellow (blue + 1) (tail string)
但输入"RYBG"
失败,返回False
而不是True
。
我做错了什么?
答案 0 :(得分:3)
除了C. Quilley的评论之外,这里有一些一般性建议和建议以不同方式构建你的函数。保护string == []
最好写成null string
,因为内置函数null
将使用单个模式匹配做出此决定,而不是必须依赖可比较的元素列表(只有当列表的元素本身具有可比性时)。模式if ... then True else False
可以缩短为...
,因为它已经是具有相同值的布尔值。 通常,尝试使用模式匹配!
我看不到你从哪里派生(red /= yellow) || (green /= blue)
。什么时候红球和黄球的数量有共同的限制?
您可能希望创建反映球的数据类型而不是String:
data Ball = Red | Green | Yellow | Blue
deriving Eq
您可能希望它们与以前一样显示:
instance Show Ball where
show Red = "R"
show Green = "G"
show Yellow = "Y"
show Blue = "B"
您可能希望在主函数中嵌入辅助函数:
isFullOfColors :: [Ball] -> Bool
isFullOfColors = go (0, 0, 0, 0)
where
go :: (Int, Int, Int, Int) -> [Ball] -> Bool
go (r,g,y,b) (Red:balls) = ...
go (r,g,y,b) (Green:balls) = ...
go (r,g,y,b) (Yellow:balls) = ...
go (r,g,y,b) (Blue:balls) = ...
go (r,g,y,b) [] = ...
关于逻辑部分的提示:这些可以表示为r,g,y和b之间的比较以及对balls
的递归调用。