所有可能的子列表方案

Posted

技术标签:

【中文标题】所有可能的子列表方案【英文标题】:All possible sublists scheme 【发布时间】:2018-12-03 08:00:17 【问题描述】:

我想找到一个列表的所有可能的连续分区:

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

我们的想法是我们逐项遍历列表,在每一步我们可以拆分或不拆分它,然后我们分支到两个新的迭代,一个有拆分,一个没有拆分。这会产生接近我想要但不完全的结果。

【问题讨论】:

您能解释一下为什么要对结果进行分组吗? 嗯,这些是将列表拆分为子列表的可能方法,不是吗? 我希望你有一个列表 ((a) (a b) ...) insted 列表列表的列表列表的某些组合列表的列表。嵌套的目的是什么? 这不会给我所有的拆分,但只有所有的子列表。我对可以将列表拆分为其子列表的所有方式都感兴趣。 【参考方案1】:

我们的目标是找到一种使用递归来描述问题的自然方式。 为了找到(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 是做什么的。 让我们来看看案例中输入和输出之间的关系

(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))的操作 列表中的一个是缺点,所以我们可以同时使用mapcons

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

现在我们已经完成了所有的拼图。我会留下最终的定义 给您的子列表。

【讨论】:

谢谢您,我添加了一个示例,说明我正在尝试做的事情,但无法让它发挥作用。是否有可能按照这些思路解决问题?想法是将其移植到 minikanren,因此辅助函数越少越好。 为什么不定义 appendo、prefixo、combino 和 sublistso 呢? 查看第 109 页和第 110 页的解决方案。基本上你想要一个可变版本的 appendo,但为了确保终止你想要排除空列表。 William E. Byrd 和 Daniel P. Friedman 的“从可变参数函数到可变参数关系的 miniKanren 视角”【参考方案2】:

既然你提到了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 定义为带有 appendoconde 和对 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)))

我从here 复制了appendo

miniKanren 中的解决方案顺序不遵循谓词定义中的目标顺序(就像在 Prolog 中那样),因为 miniKanren 将子目标产生的结果交错以实现所谓的“公平调度”。

【讨论】:

以上是关于所有可能的子列表方案的主要内容,如果未能解决你的问题,请参考以下文章

DFS 获得所有可能的解决方案?

是否有比此查询更具可扩展性的子选择替代方案?

使可扩展列表的背景变得透明似乎是不可能的。在 *** 上测试了所有解决方案

Python:枚举列表中所有元素的可能组合

Flutter集成子module联调的解决方案

ecshop首页如何调用分类下的子分类商品