2

I want to find all possible consecutive partitions of a lists:

(a b c d) => (((a) (b c d)) ((a b) (c d)) ((a b c) (d)) ((a) (b c) (d)) ((a b c d)) ((a) (b) (c) (d)))

What would be the easiest way to go about this? ideally without using counters.

Edit:

Here is an example of what I have been trying, but it doesn't quite work (it is supposed to give the answers in reverse, but that'd be ok):

(define split-list-help
  (lambda (l h a)
    (begin
      (display a)
      (if
       (null? (cdr l))
       (list (cons (cons (car l) a) h))
       (let
       [(a-nosplit (cons (car l) a))
        (h-split (if (null? a)
             (cons (list (car l)) h)
             (cons (list (car l)) (cons a h))))]
     (append  (split-list-help (cdr l) h-split '())
          (split-list-help (cdr l) h a-nosplit)))))))

(split-list-help '(a b c) '() '())

The idea is that we traverse the list item by item, at each step we can either split it or not, then we branch into two new iterations, one with splitting and one without splitting. This produces a result close to what I want but not quite.

soegaard
  • 30,661
  • 4
  • 57
  • 106

2 Answers2

3

The goal is to find a natural way of describing the problem using recursion. In order to find the sublists of (a b c d) we can focus on the element a. There are four different consecutive sublists containing a:

(a)  (a b)  (a b c)  (a b c d)

In each case we need to find the sublists of the remaining elements. All in all the result must be the collection of list that result from

combining (a)       with (sublists '(b c d))
combining (a b)     with (sublists   '(c d))
combining (a b c)   with (sublists     '(d))
combining (a b c d) with (sublists     ' ())

That is we have:

(sublists '(a b c d)) = (append (combine '(a)       (sublists '(b c d)))
                                (combine '(a b)     (sublists   '(c d)))
                                (combine '(a b c)   (sublists    '(d)))
                                (combine '(a b c d) (sublists     '())))

We note that we have described the sublists of a list four elements using a recursive call of sublists of only three elements. The base case (sublists '()) must return the empty list '().

The only remaining question is what combine does. Let's examine the relation between the input and ouput in the case

(combine '(a) (sublists '(b c d)))

The sublists of '(b c d) are:

( ((b) (c) (d))
  ((b) (c d)  )
  ((b c) (d)  )
  ((b c d)    ) )

So (combine '(a) (sublists '(b c d))) must return

( ((a) (b) (c) (d))
  ((a) (b) (c d)  )
  ((a) (b c) (d)  )
  ((a) (b c d)    ) )

The operation that preprends an element (the list '(a)) in front of a list is cons, so we can use map and cons in concert:

(define (combine x xss)
  (map (lambda (xs) (cons x xs)) ; function that prepends x to a list xs
       xss))

Now we have all pieces of the puzzle. I'll leave the final definition of sublists to you.

soegaard
  • 30,661
  • 4
  • 57
  • 106
  • Thank you, I have added an example of something I was trying to do but couldn't quite make it work. Would it be possible to solve the problem along those lines? The idea is to port it to minikanren, so the fewer helper functions and higher order functions the better. – Matías Guzmán Naranjo Jun 24 '18 at 20:11
  • Why not define appendo, prefixo, combineo and sublistso ? – soegaard Jun 24 '18 at 20:40
  • Look at page 109 and 110 for a solution. Basically you want a variadic version of appendo, but to ensure termination you want to exclude the empty list. “From Variadic Functions to Variadic Relations A miniKanren Perspective” by William E. Byrd and Daniel P. Friedman – soegaard Jun 24 '18 at 22:49
1

Since you mentioned miniKanren, here's a Prolog solution for this problem:

splits(L, LS):-                % conde ...
  (   L  = []                  % L is empty list:
  ->  LS = []
  ;                            % OR
      A = [_ | _],             % A is non-empty,
      append(A, B, L),         % for each A, B such that A + B = L,
      splits(   B, BS),        %   for every splits BS of B,   
      LS = [ A |   BS]         %     prepend A to BS to get the splits of L
  ).

%%% in SWI Prolog:
?- splits([1,2,3,4], R).
R = [[1], [2], [3], [4]] ;
R = [[1], [2], [3, 4]] ;
R = [[1], [2, 3], [4]] ;
R = [[1], [2, 3, 4]] ;
R = [[1, 2], [3], [4]] ;
R = [[1, 2], [3, 4]] ;
R = [[1, 2, 3], [4]] ;
R = [[1, 2, 3, 4]] ;
false.

Translated into miniKanren this would define splitso as a conde with an appendo and a recursive call to splitso:

#lang racket
(require minikanren)

(define (splitso L LS)
  (conde
   [(== L '()) (== LS '())]
   [(fresh (A B BS _H _T)
           (== A `(,_H . ,_T))
           (appendo A B L)
           (== LS `(,A . ,BS))    
           (splitso   B   BS))]))    

;;;
> (run* (R) (splitso '(1 2 3 4) R))
'(((1 2 3 4))
  ((1) (2 3 4))
  ((1 2) (3 4))
  ((1) (2) (3 4))
  ((1 2 3) (4))
  ((1) (2 3) (4))
  ((1 2) (3) (4))
  ((1) (2) (3) (4)))

I copied appendo from here.

The order of solutions in miniKanren does not follow the order of goals in the predicate definition (as it does in Prolog), because miniKanren interleaves the results produced by sub-goals in order to achieve what it calls "fair scheduling".

Will Ness
  • 70,110
  • 9
  • 98
  • 181