patternMinor
Safety, sharing, and laziness
Viewed 0 times
andlazinesssafetysharing
Problem
Is there a more succinct way to write code like this that has a lot of sharing between branches of the case statement?
(This is the main part of my solution to one of the Round 1 problems of the Facebook Hacker Cup 2012. The round is over.)
(This is the main part of my solution to one of the Round 1 problems of the Facebook Hacker Cup 2012. The round is over.)
type Memoizable a = a -> a
recoveries :: Int -> Memoizable (String -> Int)
recoveries m = go
where go self ('0':_) = 0
go self [] = 1
go self ss = flip rem 0xfaceb00c $ case analysis ss of
HasMany
| cValid -> cCase + bCase + aCase
| bValid -> bCase + aCase
| aValid -> aCase
Has2
| bValid -> bCase + aCase
| aValid -> aCase
Has1
| aValid -> aCase
_
| otherwise -> 0
where a = digitToInt (first ss)
b = a * 10 + digitToInt (second ss)
c = b * 10 + digitToInt (third ss)
aValid = a Analysis
analysis [] = Empty
analysis [_] = Has1
analysis [_,_] = Has2
analysis _ = HasManySolution
Hm, why did you write
For making it more succinct, I think it's easiest to have an intermediate data structure that carries both pieces of information you seek in the
The first data structure that comes to mind is a list of numbers - we just need to construct it. In this case,
Unless I've made a mistake (can't test), this should do the same as your code, and now we can write everything cleanly using pattern matches and a few guards on the intermediate list.
You could get rid of
go as a separate function? It is - as you state - the same as recoveries, why the local definition?For making it more succinct, I think it's easiest to have an intermediate data structure that carries both pieces of information you seek in the
case: Whether there are enough digits and what number the digits so far would form.The first data structure that comes to mind is a list of numbers - we just need to construct it. In this case,
scanl is our friend:import Data.List (scanl)
import Data.Char (digitToInt)
type Memoizable a = a -> a
recoveries :: Int -> Memoizable (String -> Int)
recoveries _ _ ('0':_) = 0
recoveries _ _ [] = 1
recoveries m self ss = result `rem` 0xfaceb00c
where result = case tail (scanl addDigit 0 ss) of
(_:_:c:_) | c aCase+bCase+cCase
(_:b:_) | b aCase+bCase
(a:_) | a aCase
_ -> 0
addDigit x c = x*10 + digitToInt c
aCase = self (drop 1 ss)
bCase = self (drop 2 ss)
cCase = self (drop 3 ss)Unless I've made a mistake (can't test), this should do the same as your code, and now we can write everything cleanly using pattern matches and a few guards on the intermediate list.
You could get rid of
aCase, bCase and cCase as well if you would zip the result of scanl with tails ss. But while this is more general, I felt it would have made the code harder to understand, so I left it like this.Code Snippets
import Data.List (scanl)
import Data.Char (digitToInt)
type Memoizable a = a -> a
recoveries :: Int -> Memoizable (String -> Int)
recoveries _ _ ('0':_) = 0
recoveries _ _ [] = 1
recoveries m self ss = result `rem` 0xfaceb00c
where result = case tail (scanl addDigit 0 ss) of
(_:_:c:_) | c <= m -> aCase+bCase+cCase
(_:b:_) | b <= m -> aCase+bCase
(a:_) | a <= m -> aCase
_ -> 0
addDigit x c = x*10 + digitToInt c
aCase = self (drop 1 ss)
bCase = self (drop 2 ss)
cCase = self (drop 3 ss)Context
StackExchange Code Review Q#8522, answer score: 2
Revisions (0)
No revisions yet.