【问题标题】:Prolog: partition integer list items by their parityProlog:按奇偶性划分整数列表项
【发布时间】:2015-10-10 23:31:35
【问题描述】:

编写一个谓词,将整数列表L 作为输入,并生成两个列表:包含来自L 的偶数元素的列表和来自L 的奇数元素的列表。

?- separate_parity([1,2,3,4,5,6], Es, Os).
Es = [2,4,6], Os = [1,3,5] ? ;
no

【问题讨论】:

    标签: prolog clpfd


    【解决方案1】:

    只需对列表使用结构递归。写下每个互斥情况的等价关系:

    parity_partition([A|B], [A|X], Y):- 0 is A mod 2, parity_partition(B,X,Y).
    parity_partition([A|B], X, [A|Y]):- 1 is A mod 2, parity_partition(B,X,Y).
    parity_partition([],[],[]).
    

    这意味着:关系parity_partition(L,E,O) 持有

    1. 如果L=[A|B]A 是偶数,E=[A|X]O=Y 和关系parity_partition(B,X,Y) 成立。
    2. 如果L=[A|B]A 是奇数,E=XO=[A|Y] 和关系parity_partition(B,X,Y) 成立。
    3. 如果是L=[],当E=[]O=[]

    只要写下这些等式,我们就可以使用 Prolog 程序来解决这个问题。


    在操作上,这意味着:将列表 L 分成偶数列表 E 和赔率列表 O

    1.如果`L`是一个非空列表`[A|B]`, 1a。如果 `A` 是偶数, 为`E=[H|T]`分配新的列表节点, 设置其数据字段“H=A”, 并继续分离输入列表的其余部分“B” 进入 `T` 和 `O` ;或者 1b。如果`A`是奇数, 为 `O=[H|T]` 分配新的列表节点, 设置其数据字段“H=A”, 并继续分离输入列表的其余部分“B” 进入 `E` 和 `T` ;或者 2.如果`L`是一个空列表,将`E`和`O`都设置为空列表

    实际的操作顺序可能有点不同,但概念上是一样的:

    1.尽量统一L=[A|B], E=[A|X]。如果没有,请转到 2。 1a。检查A是否是偶数。 如果没有,请放弃所做的实例化 作为统一的一部分,然后转到 2。 1b。继续使用 B、X 和相同的 O:使用 B 作为 L,使用 X 作为 E,然后转到 1。 2.尝试统一L=[A|B],O=[A|Y]。如果没有,请转到 3。 2a.检查 A 是否为奇数。 如果没有,放弃所做的实例化 作为统一的一部分,然后转到 3。 2b。继续使用 B、Y 和相同的 E:使用 B 作为 L,使用 Y 作为 O,然后转到 1。 3. 用 [] 统一 L,E,O。

    【讨论】:

      【解决方案2】:

      您可以为此使用。这样你就得到了一个纯粹的关系:

      :- use_module(library(clpfd)).
      list_evens_odds([], [], []).
      list_evens_odds([E|Zs], [E|Es], Os) :-
         0 #= E mod 2,
         list_evens_odds(Zs, Es, Os).
      list_evens_odds([E|Zs], Es, [E|Os]) :-
         1 #= E mod 2,
         list_evens_odds(Zs, Es, Os).
      

      您不仅可以使用它来将列表拆分为偶数和赔率。但你可以走得更远。以下是与 SWI 的交互,但您在 SICStus 中与 asserta(clpfd:full_answer) 的交互类似。

      ?- list_evens_odds([1, 2, 3, 4, 5, 6], Es, Os)。 Es = [2, 4, 6], os = [1, 3, 5] ; 错误的。 ?- Zs = [A,B,C], list_evens_odds(Zs, Es, Os)。 Zs = [A, B, C], Es = [A, B, C], 奥斯= [], 一个 mod 2#=0, B mod 2#=0, C mod 2#=0 ; Zs = [A, B, C], Es = [A, B], 奥斯= [C], 一个 mod 2#=0, B mod 2#=0, C mod 2#=1 ; Zs = [A, B, C], Es = [A, C], 奥斯= [B], 一个 mod 2#=0, B mod 2#=1, C mod 2#=0 ... ?- Es = [E], Os = [O], list_evens_odds(Zs, Es, Os)。 Es = [E], 奥斯 = [O], Zs = [E, O], E mod 2#=0, O mod 2#=1 ; Es = [E], 奥斯 = [O], Zs = [O, E], E mod 2#=0, O mod 2#=1 ; 错误的。

      下一个可能是最烦人的:在这里,我们询问是否存在一个既是偶数又是奇数的整数EO。当然,这样的整数是不可能存在的。但我们仍然得到两个答案!

      ?- EOs=[EO], list_evens_odds(Zs, EOs, EOs)。
      EO = [EO],
      Zs = [EO,EO],
      EO mod 2#=1,
      EO mod 2#=0 ;
      EO = [EO],
      Zs = [EO,EO],
      EO mod 2#=0,
      EO mod 2#=1 ;
      错误的。
      

      这说明了答案和解决方案之间的区别。我们在这里得到两个答案,但两者都不包含解决方案。大多数情况下,答案包含一个或多个解决方案,但也可以不包含任何解决方案,就像在这种情况下一样。这样的答案有时被称为不一致。

      不一致不一定被视为实现的错误。它们是一种工程上的权衡:与实际收益相比,确保一致性的成本可能非常高。并且: Prolog 不会产生不正确的答案:对于必须保持的条件已显示。即使那个条件被证明是错误的。

      【讨论】:

        【解决方案3】:

        这个答案是基于tpartition/4和具体化测试zodd_t/2

        :- use_module(library(clpfd)).
        

        tpartition/4zodd_t/2 结合使用,我们可以简单地编写:

        ?- tpartition(zodd_truth,[1,2,3,4,5,6],Es,Os).
        Es = [1,3,5], Os = [2,4,6].                 % succeeds deterministically
        

        【讨论】:

          【解决方案4】:

          @Will Ness 的回答很好而且很详细。如果您的 Prolog 提供,我将添加使用“高阶”内置函数(即接收谓词作为参数的谓词)的可能性:

          separate_parity(L, E, O) :-
              partition(is_even, L, E, O).
          is_even(N) :- N mod 2 =:= 0.
          

          您可以找到here 对内置函数的简要说明。

          【讨论】:

          • partition/4 的缺点:它无法扩展到约束。
          • @false: 因为急切的评估,我想...肯定 CLP(FD) 需要我更多的关注。
          • 不确定您所说的热切评估是什么意思。正是这种红色切割使 partition/4 不合适。 clpfd 版本也很急切,通过强制偶数/奇数决定。
          • @false:在库中(应用)我看不到切口。我假设它隐含在 (->)/2 中。 only Prolog 库是否有意义?
          • 一个干净的分区会期望一个谓词给出真/假或 1/0。见(这个问题)[stackoverflow.com/questions/13664870/…
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-10-09
          • 1970-01-01
          • 2017-02-06
          • 2011-12-18
          • 1970-01-01
          相关资源
          最近更新 更多