Reputation: 3547
I am trying to solve this exercise:
You are given a sequence of N balls in 4 colors: red, green, yellow and blue. The sequence is full of colors if and only if all of the following conditions are true:
- There are as many red balls as green balls.
- There are as many yellow balls as blue balls.
- Difference between the number of red balls and green balls in every prefix of the sequence is at most 1.
- Difference between the number of yellow balls and blue balls in every prefix of the sequence is at most 1.
Your task is to write a program, which for a given sequence prints
True
if it is full of colors, otherwise it printsFalse
.
My solution so far is:
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)
But it fails on the input "RYBG"
, returning False
instead of True
.
What am I doing wrong?
Upvotes: 0
Views: 121
Reputation: 16105
Besides C. Quilley's comments, here is some general advice and a suggestion for structuring your function differently. The guard string == []
is better written as null string
, since the built-in function null
will make this decision with a single pattern match, rather than having to rely on the list of elements being comparable (which it only is when the elements of the list themselves are comparable). The pattern if ... then True else False
can be shortened to ...
, since this is already a boolean with the same value. Generally, try and use pattern matching!
I cannot see where you derive (red /= yellow) || (green /= blue)
from. When does the number of red balls and yellow balls have constraints in common?
Instead of a String, you may want to create a data type that reflects balls:
data Ball = Red | Green | Yellow | Blue
deriving Eq
You may want them to be displayed just the same as before:
instance Show Ball where
show Red = "R"
show Green = "G"
show Yellow = "Y"
show Blue = "B"
And you may want to embed the helper function inside the main function:
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) [] = ...
As for hints on the logical part: These can be expressed as comparisons between r, g, y and b in combination with recursive calls to balls
.
Upvotes: 3