Prolog问题{列表}

Prolog problems {lists}

我在 prolog 中实现代码时遇到了一些问题,因为我发现它有点难以理解,因为我习惯于正常编码:(

我必须对整数列表进行排序,但它必须保留重复的值。我试图想出一个解决方案,我会使用冒泡排序,但我不知道如何在 prolog 中编写它...如果有人可以逐步解释代码并启发我,我将非常感激...

是的,我遇到的另一个问题是对由整数和数字列表组成的列表进行排序...我不知道如何从这个开始...例如:[1, 2, [4 , 1, 4], 3, 6, [7, 10, 1, 3, 9], 5, [1, 1, 1], 7]需要输出[1, 2, [1, 4, 4], 3, 6, [1, 3, 7, 9, 10], 5, [1, 1, 1], 7].

我试着写了一些代码,但它根本不起作用,我放弃了......我从互联网上复制了一些东西来解决第一个问题,但它对我来说真的没有意义......

Prolog 正常编码

它只是不是命令式[​​=96=]编码,它本质上是递归的。你需要停止用命令式的方式思考(告诉计算器做什么),你需要开始 thinking recursively(一本关于这个主题的好书。)

MergeSort 是你的朋友。它是一种稳定的递归排序算法,具有高性能并且 well-suited 符合 prolog 的工作方式。它的工作方式很简单:

  1. 根据定义,空列表是有序的。
  2. 根据定义,长度为 1 的列表也是有序的。
  3. 对于长度 > 1 的列表,
    • 将列表分成两半,
    • 对每一个进行递归排序,并且
    • 将两个 now-ordered 列表合并在一起。

仅此而已。

所以,我们需要三样东西:

  • 一个将列表分成两半的谓词,
  • 一个谓词将两个有序列表合并为一个有序列表,并且
  • 使用这 2 个助手对列表进行排序的谓词。

分区

对列表进行分区很容易。有两种终止递归的特殊情况和一种一般情况。特殊情况是微不足道的:

  • 空列表([])被分成2个空列表
    partition( [] , [] , [] ) .
    
  • 一个长度为 1 的列表被划分为自身和一个空列表:
    partition( [L] , [L] , [] ) .
    

一般情况下,长度大于 1 的列表不会更复杂。这里,我们只是从列表中取出前2项,分配给左分区,另一分配给右分区,然后我们向下递归处理余数:

partition( [L,R|Xs] , [L|Ls] , [R|Rs] ) :- partition(Xs,Ls,Rs).

把它们放在一起,你得到 partition/3:

partition( []       , []     , []     ) .
partition( [L]      , [L]    , []     ) .
partition( [L,R|Xs] , [L|Ls] , [R|Rs] ) :- partition(Xs,Ls,Rs) .

合并

将2个有序列表合并为1个有序列表同样简单。我们有 3 种终止递归的特殊情况和一种一般情况。特殊情况是一个源列表为空而另一个不为空,并且两个源列表均为空列表的情况:

merge( []     , []     , []     ) .
merge( [L|Ls] , []     , [L|Ls] ) .
merge( []     , [R|Rs] , [R|Rs] ) .

一般的递归情况,其中两个源列表是 non-empty 是一个小技巧(但不多)。你有

  • 左头在右头之前或等于右头的情况,并且
  • 左头在右头后整理的情况

我们可以使用Prolog的in-built operators for the comparison of terms来确定整理顺序:

merge( [L|Ls] , [R|Rs] , [L|Xs] ) :- L @=< R, merge(    Ls  , [R|Rs] , Xs ) .
merge( [L|Ls] , [R|Rs] , [R|Xs] ) :- L @>  R, merge( [L|Ls] ,    Rs  , Xs ) .

把它们放在一起,你得到 merge/3:

merge( []     , []     , []     ) .
merge( [L|Ls] , []     , [L|Ls] ) .
merge( []     , [R|Rs] , [R|Rs] ) .
merge( [L|Ls] , [R|Rs] , [L|Xs] ) :- L @=< R, merge(    Ls  , [R|Rs] , Xs ) .
merge( [L|Ls] , [R|Rs] , [R|Xs] ) :- L @>  R, merge( [L|Ls] ,    Rs  , Xs ) .

排序

排序也同样简单。有 2 种终止执行的特殊情况,空列表和长度为 1 的列表,两者都是按定义排序的。一般递归情况,对于长度大于 1 的列表是

  • 对列表进行分区,
  • 对每一半进行递归排序,并且
  • 合并两个 now-sorted 列表

因此:

merge_sort( []       , []  ) .
merge_sort( [X]      , [X] ) .
merge_sort( [X,Y|Zs] , Ys  ) :-
    partition( [X,Y|Zs], L0, R0 ) ,
    merge_sort(L0,Ls),
    merge_sort(R0,Rs),
    merge(Ls,Rs,Ys)
    .

整个辣酱玉米饼馅

您可以 fiddle 在 https://swish.swi-prolog.org/p/SCBlSONF.pl

partition( []       , []     , []     ) .
partition( [L]      , [L]    , []     ) .
partition( [L,R|Xs] , [L|Ls] , [R|Rs] ) :- partition(Xs,Ls,Rs) .

merge( []     , []     , []     ) .
merge( [L|Ls] , []     , [L|Ls] ) .
merge( []     , [R|Rs] , [R|Rs] ) .
merge( [L|Ls] , [R|Rs] , [L|Xs] ) :- L @=< R, merge(    Ls  , [R|Rs] , Xs ) .
merge( [L|Ls] , [R|Rs] , [R|Xs] ) :- L @>  R, merge( [L|Ls] ,    Rs  , Xs ) .

merge_sort( []       , []  ) .
merge_sort( [X]      , [X] ) .
merge_sort( [X,Y|Zs] , Ys  ) :-
  partition( [X,Y|Zs], L0, R0 ) ,
  merge_sort(L0,Ls),
  merge_sort(R0,Rs),
  merge(Ls,Rs,Ys)
  .

您可能会注意到,此程序中的大部分工作都包含每个谓词子句头部的模式匹配。这是使 Prolog 如此富有表现力和简洁的重要原因。