检查第二个列表是否是第一个列表的一半

时间:2015-05-28 17:41:01

标签: prolog

我是一名菜鸟序幕程序员,面临着我正在学习的书中给出的一个基本问题的困难。这个问题。这个问题基本上要求我们写下一个Prolog过程,该过程将两个列表作为参数,如果第一个列表的大小是第二个列表的两倍,并且两个列表以相同的元素开头,则成功。如果两个列表为空,则该过程应返回false。

例如,如果我们传递查询,它应该返回true:

a2b([a,a,a,a],[a,b]).

并且会因查询而失败:

a2b([a,a],[a,b,b,b]).

我不知道如何递归地解决这个问题,任何帮助都将不胜感激。谢谢!

3 个答案:

答案 0 :(得分:3)

首先,关于长度的请求:

/* empty list fulfills request */   
a2b_length([],[]). 
/* non-empty: discard two elements of first list, 
                one of second list, and verify
                remainder */
a2b_length([_,_|Q1],[_|Q2]) :- 
   a2b_length(Q1,Q2).

现在,我们可以添加要求"从同一个术语开始并且非空#34;并写下最后一个句子:

a2b([X,_|Q1],[X|Q2]) :-
   a2b_length(Q1,Q2).

答案 1 :(得分:1)

可爱的问题。可以使用以下代码解决:

% fail of the first element of each list don't unify
% or if one or both lists are empty
a2b([First| List1], [First| List2]) :-
    % calculate the length of the second list
    % while traversing both lists in parallel
    a2b_first(List2, 1, N, List1, Rest1),
    % check that the length of the rest of the first
    % list is equal to the length of the second list
    a2b_second(Rest1, N).

a2b_first([], N, N, Tail1, Tail1).
a2b_first([_| Tail2], N0, N, [_| Tail1], Rest1) :-
    N1 is N0 + 1,
    a2b_first(Tail2, N1, N, Tail1, Rest1).

a2b_second([], 0).
a2b_second([_| Tail1], N) :-
    M is N - 1,
    a2b_second(Tail1, M).

当然,解决方案有一个更简单(但不是很有趣!)的解决方案:

% fail of the first element of each list don't unify
% or if one or both lists are empty
a2b([First| List1], [First| List2]) :-
    length([First| List1], N1),
    length([First| List2], N2),
    N1 is 2 * N2.

length/2谓词通常可以作为内置谓词或库谓词使用。

为了学习Prolog,研究第一个解决方案很有意思。例如,它举例说明了如何利用第一个参数索引以及如何使用累加器来编写尾递归的谓词(从而节省空间)。

此外,第一种解决方案可以比第二种解决方案更有效。在第二个解决方案中,我们总是遍历两个列表以找到它们的长度。但是,在第一个解决方案中,并不总是必要的。

答案 2 :(得分:0)

不要过分思考:只需描述解决方案,让Prolog对其进行排序。

解决方案不需要计数或谓词,而不是其琐碎的自我。它是所有模式匹配。我们有一个特殊的(终止案例),断言长度为2的列表是长度为1的列表的两倍(这应该是非常明显的):

is_twice_as_long_as( [_,_] , [_] ) .

然后是一般情况,断言给定两个任意长度的列表,左边是右边IF的两倍,我们可以(A)从左边删除2个项目,(B)从右边删除1个项目,并递归断言他们各自的剩余部分同样是两倍:

is_twice_as_long_as( [_,_|A] , [_|B] ) :- is_twice_as_long_as( A , B ) .

给我们成品:

is_twice_as_long_as( [_,_]   , [_]   ) .
is_twice_as_long_as( [_,_|A] , [_|B] ) :- is_twice_as_long_as( A , B ) .

容易!

编辑注意要求两个列表以相同的元素开头:

取决于如何解释......

  • 这要求列表在每次迭代时都有一个共同的头:

    is_twice_as_long_as( [A,_]   , [A]   ) .
    is_twice_as_long_as( [A,_|L] , [A|R] ) :- is_twice_as_long_as( L , R ) .
    
  • 这只检查一次普通头::

    is_twice_as_long_as( [A|As] , [A|Bs] ) :-
      is_2x([A|As],[A|Bs]) .
    
    is_2x( [_,_]   , [_] ) .
    is_2x( [_,_|L] , [_|R] ) :- is_2x( L , R ) .