【问题标题】:Sort predicate in PrologProlog中的排序谓词
【发布时间】:2020-12-15 09:06:56
【问题描述】:

我想弄清楚如何在 Prolog 中实现 sort/2。我想看看它是如何工作的,但我在任何地方都找不到它的代码。有人知道它是如何实现的吗?

【问题讨论】:

  • 这能回答你的问题吗? Sorting a list in Prolog
  • 我想做的是按降序排列第二个元素而不删除重复项。我现在已经完成了类似“sort4(List,Sorted):-sort(2,@>=,List,Sorted)”。但是我不允许使用其他任何东西然后 sort/2 所以我认为我可以更改原始函数并将其实现为辅助函数。

标签: prolog swi-prolog


【解决方案1】:

在 SWI-Prolog 中,sort/2 是“内置”的,所以它在 C 中。

该文件似乎是发行版的src/pl-lists.c

这里是:

https://github.com/SWI-Prolog/swipl-devel/blob/master/src/pl-list.c

在第 543 行:

static
PRED_IMPL("sort", 2, sort, PL_FA_ISO)
{ PRED_LD

  return pl_nat_sort(A1, A2,
             TRUE, SORT_ASC,
             0, NULL, FALSE PASS_LD);
}

pl_nat_sort 在同一个文件中

这条评论在历史上很有趣:

自然归并排序。代码由 Richard O'Keefe 贡献并集成 Jan Wielemaker 进入 SWI-Prolog。关于这段代码的好处是 它不使用额外的空间并且性能非常稳定。 理查兹声称 libc 中的许多 qsort() 实现非常 减缓。这不是 glibc 2.2 的情况,它执行大约 与之前基于 qsort() 的实现相同。

大概是 Richard O'Keefe 说的:

自从大约以来,我一直在排序实用程序中使用此代码的变体 1988. 它使 UNIX sort(1) 程序尘埃落定。如你所知, sort(1) 将输入分成适合内存的块,对 使用 qsort() 块,并将块写入磁盘,然后合并 块。对于适合内存的文件,此代码的变体运行 大约是 sort(1) 的两倍。其中一部分是更好的 I/O,但部分是 只是简单地不使用 qsort()。

代姆。这让我想起了在 89 年编写排序算法的记忆。

【讨论】:

    【解决方案2】:

    如果您只是在寻找任何排序算法,您总是可以使用bubblesort *cough*。这是对列表进行排序的最简单和低效的方法之一,但是 - 根据实现 - 将在线性时间内遍历已经排序的列表。冒泡排序不会删除重复项。此实现按降序排列:

    bubblesort(List, SortedList) :-
        bubbleit(List, List1), 
        ! ,
        bubblesort(List1, SortedList) .
    bubblesort(List, List).
    
    bubbleit([X,Y|Rest], [Y,X|Rest]) :-
        X < Y, 
        ! .
    bubbleit([Z|Rest], [Z|Rest1]) :-
        bubbleit(Rest, Rest1).
    
    ?- bubblesort([1,2,5,4,7,3,2,4,1,5,3],L).
    L = [7, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1].
    

    如果您想对列表中的第二个元素[_,E|] 进行排序,请将第一个bubbleit 规则更改为

    bubbleit([X,Y|Rest], [Y,X|Rest]) :-
        X = [_,XX|_],
        Y = [_,YY|_],
        XX < YY, 
        ! .
    
    ?- bubblesort([[a,3],[b,5],[c,1],[d,4]],L).
    L = [[b, 5], [d, 4], [a, 3], [c, 1]].
    

    【讨论】:

    • 谢谢!如果你有一个列表列表 [[ , ], [ , ]] 并且想按每个子列表的第二个元素排序,它会如何工作?
    • @fewfrg 我在答案中添加了一个方法
    • @fewfrg ^^ 您可以通过单击upvote/donvote按钮下方答案左侧的灰色复选标记来接受答案
    【解决方案3】:

    尝试搜索快速排序。

    这是一个链接:https://www.codepoc.io/blog/prolog/4934/prolog-program-to-sort-a-list-using-quick-sort

    这是另一个例子:-

    quicksort([], []).
    quicksort([X | Tail], Sorted):-
        split(X, Tail, Small, Big),
        quicksort(Small, SortedSmall),
        quicksort(Big, SortedBig),
        concatenate(SortedSmall, [X| SortedBig], Sorted).
    
    split(X, [], [], []).
    split(X, [Y| Tail], [Y | Small], Big):-
        X > Y, !,
        split(X, Tail, Small, Big).
    split(X, [Y| Tail], Small, [Y | Big]):-
        split(X, Tail, Small, Big).
    
    concatenate([],List,List).
    concatenate([Item|List1],List2,[Item|List3]) :-
        concatenate(List1,List2,List3).
    
    
    ?-quicksort([1,7,4,3,6,5,9,8,12,1],L).
    L = [1, 1, 3, 4, 5, 6, 7, 8, 9, 12]
    false
    

    【讨论】:

    • 有更简单的方法吗?我想做的是按降序排列第二个元素而不删除重复项。我现在已经完成了类似“sort4(List,Sorted):-sort(2,@>=,List,Sorted)”。但是我不允许使用其他任何东西然后 sort/2 所以我认为我可以更改原始函数并将其实现为辅助函数。
    【解决方案4】:

    归并排序 算法非常适合 Prolog。更不用说以递归和列表为基础的语言实现微不足道了。

    这是一个 SWI 游乐场:https://swish.swi-prolog.org/p/swi-prolog-merge-sort.pl

    merge_sort( [], [] ).             % The empty list is by definition ordered
    merge_sort( [X], [X] ).           % So is a list of length one
    merge_sort( Unsorted, Sorted ) :- % otherwise...
      partition( Unsorted, L, R ) ,   % partition the list into 2 halves
      merge_sort(L,L1),               % recursively sort the left half
      merge_sort(R,R1),               % recursively sort the right half
      merge(L1,R1,Sorted)             % merge the newly-sorted halves
        .                             % See? simple!
    
    partition( []      , []     , []     ).   % the empty list gets partitioned into two empty lists
    partition( [X]     , [X]    , []     ).   % a left of length 1 gets partitioned into itself and an empty list
    partition( [X,Y|L] , [X|Xs] , [Y|Ys] ) :- % list of length 2 or more gets popped and divided between left and right halves
      partition(L,Xs,Ys)                      % with the remainder being recursively partitioned.
        .                                     % Easy!
    
    merge( []     , []     , []     ).        % merging to empty lists is trivial
    merge( []     , [Y|Ys] , [Y|Ys] ).        % merging an empty list and a non-empty list is easy
    merge( [X|Xs] , []     , [X|Xs] ).        % merging a non-empty list and an empty list is easy
    merge( [X|Xs] , [Y|Ys] , [Lo,Hi|Zs] ) :-  % otherwise...
      compare(X,Y,Lo,Hi),                     % compare the two terms, put them in collating sequence
      merge(Xs,Ys,Zs)                         % and recursively merge the tails
      .                                       % Easy!
    
    compare( X , Y , X, Y ) :- X @=< Y. % if X <= Y, X is Lo, and Y is Hi
    compare( X , Y , Y, X ) :- X @>  Y. % if X >  Y, Y is Lo, and X is Hi
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多