【问题标题】:Prolog - Recursively append numbers to a listProlog - 递归地将数字附加到列表中
【发布时间】:2020-11-14 01:15:44
【问题描述】:

我刚刚开始学习 Prolog,但我在理解递归概念时遇到了麻烦。现在,仅出于练习的目的,我正在尝试编写一个程序,将 10 个数字附加到一个列表中,然后打印出该列表。

这个程序的自我强加规则是列表必须在主谓词中“声明”(我不确定这是否是 Prolog 的正确词),它调用另一个谓词将数字附加到列表中.

这是我到目前为止所拥有的,我知道它不会起作用,因为我试图在 addToList 谓词的末尾重新定义 List,这在语言中是不允许的。

% Entry point that declares a list (`List`) to store the 10 numbers
printList(List) :-
    addToList(0, List),
    writeln(List).

% Base case - once we hit 11 we can stop adding numbers to the list
addToList(11, _).

% First case - this predicate makes adding the first number easier for me...
addToList(0, List) :-
    append([], [0], NewList),
    addToList(1, NewList),
    append([],  NewList, List). % This is valid, but List will just be [0] I think..

% Cases 1-10
addToList(Value, List) :-
    append(List, [Value], NewList),
    NextVal is Value+1,
    addToList(NextVal, NewList),
    append([], NewList, List). % This is INVALID since List is already defined

这个程序将开始于:

printList(List).

有没有一种简单的方法可以更改我编写的损坏程序以使其正常工作?我对如何获取存储在List 中的数字非常迷茫。

【问题讨论】:

    标签: list recursion prolog


    【解决方案1】:

    你在程序上思考,在序言中你不能改变变量。您正在尝试自己构建列表。在 prolog 样式中,您尝试声明所需列表的约束。如果 nlist/2 是一个给出 N 个数字列表的谓词,那么它的属性到底是什么? nlist(0, []). 如果是 nlist(N, Xs) 然后是 nlist(N+1, [N+1 | Xs])。所以你只需编写这些,让 prolog 负责构建。

    nlist(0, []).
    nlist(N, [N | Xs]) :-
        N>0, N1 is N-1,
        nlist(N1, Xs).
    

    如果您对递归调用的发生方式感到困惑,请尝试使用trace/0trace/1。您可以在以下跟踪中看到调用是如何完成的。您可以致电trace(nlist) 获取此信息。

    ?- nlist(3, X).
     T Call: nlist(3, _78)
     T Call: nlist(2, _902)
     T Call: nlist(1, _1464)
     T Call: nlist(0, _2026)
     T Exit: nlist(0, [])
     T Exit: nlist(1, [1])
     T Exit: nlist(2, [2, 1])
     T Exit: nlist(3, [3, 2, 1])
    X = [3, 2, 1]
    

    更程序化的样式代码如下

    addToList(11, A, A).
    
    % Cases 1-10
    addToList(Value, List, NewList) :-
        Value < 11,  append(List, [Value], Temp),
        NextVal is Value+1,
        addToList(NextVal, Temp, NewList).
    

    这里给出的中间参数是累加器。当您达到 11 岁时,累加器就是答案。

    ?- addToList(1, [], X).
    X = [1, 2, 3, 4, 5, 6, 7, 8, 9|...] 
    
    ?- addToList(5, [], X).
    X = [5, 6, 7, 8, 9, 10] 
    

    查看示例跟踪以及它们在nlistaddToList 中的区别。尝试找出差异以及发生这种情况的原因。

    ?- addToList(7, [], X).
     T Call: addToList(7, [], _33565254)
     T Call: addToList(8, [7], _33565254)
     T Call: addToList(9, [7, 8], _33565254)
     T Call: addToList(10, [7, 8, 9], _33565254)
     T Call: addToList(11, [7, 8, 9, 10], _33565254)
     T Exit: addToList(11, [7, 8, 9, 10], [7, 8, 9, 10])
     T Exit: addToList(10, [7, 8, 9], [7, 8, 9, 10])
     T Exit: addToList(9, [7, 8], [7, 8, 9, 10])
     T Exit: addToList(8, [7], [7, 8, 9, 10])
     T Exit: addToList(7, [], [7, 8, 9, 10])
    X = [7, 8, 9, 10] 
    

    【讨论】:

    • 谢谢,感谢您的回复。您的解决方案简单易懂。随着我继续学习 Prolog,让自己摆脱我的程序性思维会很困难。
    • 你会慢慢习惯的。您也可以编写过程式代码,但您将与 prolog 环境作斗争,而不是让它帮助您。
    【解决方案2】:

    这是我的解决方案:

    printSeries(_,[],0):-!.
    printSeries(S,[S|T],C):-
        S1 is S+1,
        C1 is C-1,
        printSeries(S1,T,C1).
    
    ?- printSeries(7,L,5).
    L = [7, 8, 9, 10, 11]
    

    谓词可用于打印使用起始数字的任何系列以及想要增加多少次。一个非常简单的方法是使用计数器。第一个谓词是说,不管起始编号是什么,也不管列表中的内容是什么,如果计数器达到 0,程序应该停止(意思是停止)。第二个谓词我们有起始编号,以及我们告诉它的列表,您必须以起始编号开始列表,最后是计数器。接下来,我们将起始数字加 1。将计数器减 1。然后通过将新值赋予谓词来重做所有事情。

    ?-printSeries(1,L,10).
    L = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    

    【讨论】:

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