1 
{-
   2 
 
   3 
Pearl 6: Making a century
   4 
 
   5 
Problem:
   6 
Given the digits 1..9, list all the ways the operations + and x can be inserted into
   7 
the sequence so as to make the total sum 100.
   8 
For example:
   9 
100 = 12 + 34 + 5x6 +7 + 8 + 9
  10 
100 = 1 + 2x3 + 4 + 5 + 67 + 8 + 9
  11 
 
  12 
No parthneses, normal order of operations.
  13 
 
  14 
Solution:
  15 
This is a brute-force problem, but the goal of the pearl is to establish some
  16 
general formulation of 'Brute-Force' search, and improve on it with 'little'
  17 
assumptions.
  18 
 
  19 
Two main things for brute-force methods:
  20 
1. Generating the 'values' while generating the 'candidates', can produce savings (Especially
  21 
if generations of candidates can be 'sequential').
  22 
2. Reduce 'good' critertia to 'ok', it can help trim the generation of all sequences.
  23 
 
  24 
-}
  25 
 
  26 
-- You CAN skip all the general formulation description and go to ======
  27 
 
  28 
-- General formulation:
  29 
-- candidates    :: Data -> [Candidate]
  30 
-- value        :: Candidate -> Value
  31 
-- good         :: Value -> Bool
  32 
 
  33 
-- Brute force search over all candidates
  34 
-- solutions     :: Data -> [Candidate]
  35 
-- solutions    =  filter (good.value).candidates   
  36 
 
  37 
-- Now, using a few assumptions we can modify thigns:
  38 
 
  39 
-- Assumption 1: This will allow us to fuse operations.
  40 
--
  41 
-- Data is a list of values, [Datum], and candidates takes the form
  42 
-- candidates :: [Datum] -> [Candidate]
  43 
-- candidates =  foldr extend []
  44 
-- where
  45 
-- extend :: Datum -> [Candidate] -> [Candidate]
  46 
--
  47 
--
  48 
-- Assumption 2: This will allow us to extend search only as needed.
  49 
--
  50 
--      2.a There is a predicate 'ok', such that every good value is necesserialy 'ok'
  51 
--      2.b candidates with 'ok' values are the extension of candidates with 'ok' value.
  52 
--
  53 
-- Assumption 3: This will save us computing 'value' of candidate
  54 
--
  55 
--   map value.extend x = modify x.map value
  56 
--  The values of an extended set can be computed from the values of which the extension was
  57 
--  built from.
  58 
--
  59 
--  We then introduce a few operations on 'fork', 'cross', and relates these
  60 
-- to zip and unzip.
  61 
--   fork (f,g) x          =  (f x, g x)
  62 
--   cross (f,g) (x,y)     = (f x, g y)
  63 
--
  64 
-- and we get:
  65 
 
  66 
-- solutions     ::     Data -> [Candidate]
  67 
-- solutions     =     map fst.filter (good.snd).foldr expand []
  68 
 
  69 
-- expand x     =     filter (ok.snd).zip.cross (extend x, modify x).unzip
  70 
 
  71 
module P6 where
  72 
 
  73 
import Data.List (intercalate)
  74 
 
  75 
-- ================================================
  76 
 
  77 
 
  78 
 
  79 
-- So all the above was general design-framework.
  80 
-- now, to the specific problem (creating 100)
  81 
 
  82 
-- Expression     = sum of terms
  83 
-- Term         = products of factors
  84 
-- Factor         = sequence of digits
  85 
 
  86 
                            -- Candidate solution are expressions built from + and x
  87 
                            -- Remember: No parnthesis etc, and simple prioity.
  88 
type Expression    = [Term]    -- Each expression is the sum of Terms
  89 
type Term         = [Factor]    -- Each term is the product of factors
  90 
type Factor     = [Digit]    -- Each factor is list of digits
  91 
type Digit         = Int   
  92 
 
  93 
 
  94 
-- Just computing the value:
  95 
valExpr        ::    Expression -> Int
  96 
valExpr         =    sum.map valTerm
  97 
valTerm      = product.map valFact
  98 
valFact      = foldl (\n d -> 10*n+d)  0
  99 
 
 100 
 
 101 
good     ::     Int -> Bool
 102 
good v     =     (v==100)
 103 
 
 104 
ok        ::    Int -> Bool
 105 
ok v     =     (v<=100)
 106 
 
 107 
-- Creating all possible expressions
 108 
-- They mention there is a function 'partitions', but never spell it out.
 109 
-- so here it is, even though we will NOT use it!
 110 
partitions :: [Digit] -> [[[Digit]]]
 111 
partitions [] = [[]]
 112 
partitions (x:xs) = [[x]:p | p <- partitions xs]                -- x is a prt of it's own
 113 
                 ++ [(x:ys):yss | (ys:yss) <- partitions xs]    -- x is with the next ones
 114 
 
 115 
-- A difffernet way
 116 
expressions xs = foldr extend [] xs
 117 
 
 118 
extend                     ::    Digit -> [Expression] -> [Expression]
 119 
extend x []             =    [[[[x]]]]
 120 
extend x es             =    concatMap (glue x) es
 121 
 
 122 
-- glue: Each new digit can be added in one of 3-different ways to the expression:
 123 
-- as a digit; as factor (multiplication); or as a term ==> 3 options.
 124 
-- BTW, this shows the number of possible expressions is 3^(n-1)
 125 
glue                     ::    Digit -> Expression -> [Expression]
 126 
glue x ((xs:xss):xsss)    =     [((x:xs):xss):xsss,       -- digit
 127 
                             ([x]:xs:xss):xsss,        -- factor
 128 
                             [[x]]:(xs:xss):xsss]    -- term
 129 
 
 130 
 
 131 
-- Main
 132 
 
 133 
main = do
 134 
    -- putStr "Testing partitions: "
 135 
    -- print $ partitions [1..4]
 136 
 
 137 
    putStrLn "Brute force : "
 138 
    let res = filter (good.valExpr) $ expressions [1..9]
 139 
    --print res
 140 
    prettyPrint res
 141 
 
 142 
 
 143 
-- pretty
 144 
prettyPrint es         =     putStr $ unlines $ map printExpression es
 145 
 
 146 
printExpression     ::  Expression -> String
 147 
printExpression e     =     "100 = " ++
 148 
                        ( intercalate  " + " $ map printTerm e)
 149 
 
 150 
printTerm t         =      intercalate  "*" $ map printFactor t
 151 
printFactor f         =      concat $ map show f