List monad provides an excellent abstraction for backtracking in search problems. However, the problem that I am currently facing is that it includes a condition in addition to retreat. (It also includes restrictions related to previous choices made in the search path, but I will attack this problem later.)
The following simplified example illustrates the issue. the function sumTois given a non-negative integer and a list with pairs of integers. The first element in each pair is a positive integer, the second element is the number of such integers. The search problem is to express the first argument using integers in a list with counter restrictions. For example, here the integer 8 is represented differently as the sum of five 1s, three 2and two
4, taking into account the fact that all the numbers making up the sum should even (therefore, 1cannot be used).
λ> sumTo 8 [(1,5), (4,2), (2,3)]
[[4,4],[4,2,2],[2,2,4],[2,4,2]]
Below is my current recursive solution to the problem.
sumTo :: Int -> [(Int, Int)] -> [[Int]]
sumTo = go []
where
go :: [(Int, Int)] -> Int -> [(Int, Int)] -> [[Int]]
go _ 0 _ = [[]] -- base case: success
go _ _ [] = [] -- base case: out of options, failure
-- recursion step: use the first option if it has counts left and
-- is suitable; append to this cases where current option is not
-- used at this point
go prevOpts n (opt@(val,cnt):opts) =
(if cnt > 0 && val <= n && even val
then map (val:) $ go [] (n - val) $ (val,cnt-1):(prevOpts ++ opts)
else [])
++ go (opt:prevOpts) n opts
While the function works fine, it is much more complicated than the one without state, using the list monad.
sumToN :: Int -> [Int] -> [[Int]]
sumToN 0 _ = [[]]
sumToN n opts = do
val <- opts
guard $ val <= n
guard $ even val
map (val:) $ sumToN (n - val) opts
Without limitations, this gives one additional solution.
λ> sumToN 8 [1, 4, 2]
[[4,4],[4,2,2],[2,4,2],[2,2,4],[2,2,2,2]]
, - ,
StateT - ,
.