Permalink
Browse files

Refactored Haskell solutions to no longer need the CodeJam module.

With some more experience, I've found that the CodeJam module is not
really necessary, so I refactored the Haskell solutions (with heavy
commentary) to make them easier to understand and, in some cases,
shorter.
  • Loading branch information...
1 parent e0debcc commit a79f028c1e0cade6a69c791266d08cfd1915bda4 Damien Radtke committed Aug 27, 2012
Showing with 118 additions and 63 deletions.
  1. +39 −15 alien-language/alien-language.hs
  2. +30 −16 minimum-scalar/minimum-scalar.hs
  3. +16 −5 reverse-words/reverse-words.hs
  4. +33 −27 store-credit/store-credit.hs
@@ -1,38 +1,62 @@
-{-# OPTIONS_GHC -i.. #-}
-
{-
- URL: http://code.google.com/codejam/contest/90101/dashboard#s=p0
-}
-import CodeJam
-
+import Control.Monad
type Pattern = [String]
main = do
- input <- fmap lines getContents
- let (l:d:n:_) = map read $ words $ head input :: [Int]
- let (knownWords,patterns) = splitAt d $ tail input
- let input = map (:[]) patterns
- codeJam (solve knownWords) input
-
-solve :: Input -> Input -> String
-solve knownWords patterns = show $ foldl f 0 knownWords
- where pattern = convertPattern $ head patterns
- f acc word = if word `isMatch` pattern then acc + 1 else acc
-
+ -- Read the first three variables: L (word length),
+ -- D (number of known words), and N (number of test cases)
+ (l:d:n:_) <- readIntList
+
+ -- Read in the next D lines, which contain the known words
+ knownWords <- getLines d
+
+ -- Solve
+ mapM (solve knownWords) [1..n]
+
+-- Solve the problem, given a list of known words and the case number
+solve :: [String] -> Int -> IO ()
+solve knownWords i = do
+ -- Read in the pattern and convert it
+ pattern <- liftM convertPattern getLine
+
+ -- Iterate over the known words, comparing the pattern to each one
+ let ans = countMatches knownWords pattern
+ putStrLn $ "Case #" ++ show i ++ ": " ++ show ans
+
+-- Count the number of known words that match the pattern
+countMatches :: [String] -> Pattern -> Int
+countMatches knownWords pattern = foldl f 0 knownWords
+ where f acc word = if word `isMatch` pattern then acc+1 else acc
+
+-- Create a pattern from a string. Each element of a pattern is a list
+-- of characters that are valid in that position
convertPattern :: String -> Pattern
convertPattern [] = []
convertPattern p@(x:xs) =
if [x] == "("
then let (a,b) = extractGroup p in a : convertPattern b
else [x] : convertPattern xs
+-- Extract a parenthetical group from a string, used for creating patterns
extractGroup :: String -> (String,String)
extractGroup pattern = (group,rest)
where pattern' = tail pattern
group = takeWhile (\x -> [x] /= ")") pattern'
rest = drop (length group + 1) pattern'
+-- Returns true if the string matches the pattern
isMatch :: String -> Pattern -> Bool
isMatch [] _ = True
isMatch (x:xs) (y:ys) = if x `elem` y then isMatch xs ys else False
+
+-- Read a list of Int's from standard input
+readIntList :: IO [Int]
+readIntList = getLine >>= \l -> return (fmap read $ words l)
+
+-- Get the next n lines from standard input
+getLines :: Int -> IO [String]
+getLines n = foldM f [] [1..n]
+ where f l _ = getLine >>= \x -> return $ x:l
@@ -1,25 +1,39 @@
-{-# OPTIONS_GHC -i.. #-}
-
{-
- URL: http://code.google.com/codejam/contest/32016/dashboard#s=p0
-}
-import CodeJam
+import Control.Monad
import Data.List
-main :: IO ()
-main = codeJam' solve
+-- Read in the number of cases and call 'solve' for each one
+main = readLn >>= \n -> mapM solve [1..n]
-solve :: Input -> String
-solve input = show result
- where (l1:l2:l3:_) = input
- n = read l1 :: Int
- v1 = parseVector l2 n
- v2 = parseVector l3 n
- result = scalar (sort v1) (reverse $ sort v2)
+-- Solve a case, passing in the case number
+solve :: Int -> IO ()
+solve i = do
+ -- Read in the number of items per vector. Not used,
+ -- but we need to read it in order to get past it
+ readLn :: IO (Int)
+
+ -- Read each vector in as a list of Ints, then sort the
+ -- first one and reverse-sort the second one
+ v1 <- readIntList >>= \l -> return (sort l)
+ v2 <- readIntList >>= \l -> return (reverse $ sort l)
+
+ -- Calculate the scalar
+ let result = scalar v1 v2
+
+ -- Display the result
+ putStrLn $ "Case #" ++ show i ++ ": " ++ show result
-parseVector :: String -> Int -> [Integer]
-parseVector line n = map read $ take n $ (words line) :: [Integer]
+-- Read a list of Int's from standard input
+readIntList :: IO [Int]
+readIntList = getLine >>= \l -> return (fmap read $ words l)
-scalar :: [Integer] -> [Integer] -> Integer
-scalar v1 v2 = sum $ zipWith (*) v1 v2 :: Integer
+-- Calculate the scalar value of two vectors. The input vectors
+-- are first converted from [Int] to [Integer] to prevent potential
+-- overflows
+scalar :: [Int] -> [Int] -> Integer
+scalar v1 v2 = sum $ zipWith (*) v1' v2'
+ where v1' = map toInteger v1
+ v2' = map toInteger v2
@@ -1,10 +1,21 @@
-{-# OPTIONS_GHC -i.. #-}
-
{-
- URL: http://code.google.com/codejam/contest/dashboard?c=351101#s=p1
-}
-import CodeJam
+import Control.Monad
-main :: IO ()
-main = codeJam' (\input -> unwords.reverse.words $ head input)
+-- Read in the number of cases and call 'solve' for each one
+main = readLn >>= \n -> mapM solve [1..n]
+
+-- Solve a case, passing in the case number
+solve :: Int -> IO ()
+solve i = do
+ -- Read in the next line
+ line <- getLine
+
+ -- Convert the line to a list of words, reverse it, then
+ -- convert it back to a string
+ let ans = (unwords.reverse.words) line
+
+ -- Print the answer
+ putStrLn $ "Case #" ++ show i ++ ": " ++ ans
@@ -1,27 +1,33 @@
-{-# OPTIONS_GHC -i.. #-}
-
-{-
- - URL: http://code.google.com/codejam/contest/351101/dashboard#s=p0
- -}
-
-import CodeJam
-
-data Item = Item { index :: Int, price :: Int }
-
-instance Show Item where
- show (Item index price) = show index
-
-main :: IO ()
-main = codeJam' solve
-
-solve :: Input -> String
-solve input = (show res1) ++ " " ++ (show res2)
- where (l1:l2:l3:_) = input
- credit = read l1 :: Int
- items = read l2 :: Int
- prices = map read $ take items $ (words l3) :: [Int]
- itemlist = zipWith Item [1..] prices
- options = [(item1,item2) | item1 <- itemlist, item2 <- itemlist,
- (index item1) < (index item2),
- (price item1) + (price item2) == credit]
- (res1,res2) = head options
+{-
+ - URL: http://code.google.com/codejam/contest/351101/dashboard#s=p0
+ -}
+
+import Control.Monad
+
+-- Read in the number of cases and call 'solve' for each one
+main = readLn >>= \n -> mapM solve [1..n]
+
+-- Solve a case, passing in the case number
+solve :: Int -> IO ()
+solve i = do
+ -- Read in the variables for this case
+ credit <- readLn :: IO (Int)
+ items <- readLn :: IO (Int)
+ prices <- readIntList
+
+ -- Create a list of item tuples, which associates index with price
+ let itemList = zip [1..] prices
+
+ -- Use a list comprehension to find all item pairs whose prices
+ -- add up to the available credit. Since we know there will be
+ -- one and only one, we can take the head of the list and extract
+ -- the indices of each item
+ let ((j,_),(k,_)) = head $ [(a,b) | a <- itemList, b <- itemList,
+ (fst a) < (fst b), (snd a) + (snd b) == credit]
+
+ -- Display the result
+ putStrLn $ "Case #" ++ show i ++ ": " ++ show j ++ " " ++ show k
+
+-- Read a list of Int's from standard input
+readIntList :: IO [Int]
+readIntList = getLine >>= \l -> return (fmap read $ words l)

0 comments on commit a79f028

Please sign in to comment.