Все возможные схемы подсписков

Я хочу найти все возможные последовательные разделы списков:

(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)))

Какой самый простой способ это сделать? в идеале без использования счетчиков.

Редактировать:

Вот пример того, что я пытался, но это не совсем работает (предполагается, что ответы даются наоборот, но это было бы нормально):

(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) '() '())

Идея состоит в том, что мы пересекаем список по пунктам, на каждом шаге мы можем либо разделить его, либо нет, затем мы разветвляемся на две новые итерации, одну с разбиением и одну без разбиения. Это дает результат, близкий к тому, что я хочу, но не совсем.

2 ответа

Решение

Цель состоит в том, чтобы найти естественный способ описания проблемы с помощью рекурсии. Для того, чтобы найти списки (a b c d) мы можем сосредоточиться на элементе a, Существует четыре разных последовательных списка, содержащих a:

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

В каждом случае нам нужно найти подсписки оставшихся элементов. В общем, результат должен быть коллекцией списка, полученного в результате

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     ' ())

То есть имеем:

(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     '())))

Отметим, что мы описали подсписки списка из четырех элементов, используя рекурсивный вызов подсписков только из трех элементов. Базовый случай (sublists '()) должен вернуть пустой список '(),

Единственный оставшийся вопрос - что делает комбайн? Давайте рассмотрим отношение между входом и выходом в случае

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

Сублилисты '(b c d) являются:

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

Так (combine '(a) (sublists '(b c d))) должен вернуться

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

Операция, которая предопределяет элемент (список '(a)) перед списком есть минусы, поэтому мы можем использовать map а также cons на концерте:

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

Теперь у нас есть все кусочки головоломки. Я оставлю вам окончательное определение подсписков.

Поскольку вы упомянули miniKanren, вот решение Prolog для этой проблемы:

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.

В переводе на miniKanren это будет определять splitso как conde с appendo и рекурсивный вызов 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)))

Я скопировал appendo отсюда

Порядок решений в miniKanren не соответствует порядку целей в определении предиката (как в Prolog), потому что miniKanren чередует результаты, полученные подцелями, для достижения того, что он называет "справедливым планированием".

Другие вопросы по тегам