【问题标题】:Prolog - Add element to List in a recursive predicateProlog - 在递归谓词中将元素添加到列表
【发布时间】:2014-09-11 19:08:43
【问题描述】:

我需要构建一个 Prolog 谓词,它在 Books2009 中返回 2009 年出版的书籍列表,在 Books2014 中返回 2014 年出版的书籍。

setbooks:将仅用于“创建”将在谓词 booksList_2009_2014 中使用的图书列表。
booksList_2009_2014(L, Books2009, Books2014):这是我们试图创建和运行的谓词(“L”是以前在 setbooks 中设置的书籍列表)。

我的代码解释:我尝试制作将插入元素的谓词insertAtEnd(稍后将在booksList_2009_2014中使用)在列表的末尾。
然后我写了谓词 booksList_2009_2014 用于最简单的情况(接收一个空的书籍列表),然后接收一个只有 1 个元素的书籍列表,然后,一个递归的情况可以完成这项工作不论多长的书单。

这是我的代码文件:

setbooks([book('Name 5', 'Publisher ABC', date(12, 2014)), book('Name 4', 'Publisher ABC', date(05, 2009)), book('Name 3', 'Publisher ABC', date(02, 2009)), book('Name 6', 'Publisher ABC', date(03, 2013)), book('Name 2', 'Publisher ABC', date(12, 2014)), book('Name 1', 'Publisher ABC', date(06, 2009))]).

insertAtEnd(X,[ ],[X]).
insertAtEnd(X,[H|T],[H|Z]) :- insertAtEnd(X,T,Z).

booksList_2009_2014([],_,_).

booksList_2009_2014([book(Name,Publisher,date(M1,Y1))],Books2009, Books2014):-
    Y1=2014 -> insertAtEnd(book(Name,Publisher,date(M1,Y1)),_,Books2014);
    Y1=2009 -> insertAtEnd(book(Name,Publisher,date(M1,Y1)),_,Books2009).

booksList_2009_2014([H|T],Books2009, Books2014):-
    booksList_2009_2014(T,Books2009, Books2014).  

我不确定,但我认为递归代码中某处的代码不正确:

booksList_2009_2014([H|T],Books2009, Books2014):-
        booksList_2009_2014(T,Books2009, Books2014).  

当我输入 setbooks(L),booksList_2009_2014(L,Books2009, Books2014) 时,程序只返回列表中的最后一本书。例如:

Books2009 = [book('Name 1', 'Publisher ABC', date(06, 2009))] 

有人可以帮助我吗?拜托了???

【问题讨论】:

    标签: list recursion prolog


    【解决方案1】:

    如何解释? 您正在反转CONS操作WRT控制流...... 并且 insertAtEnd 无论如何它是 Prolog 中的错误方法,因为它是一种语言 不可变(分配一次)变量。

    你可以写

    booksList_2009_2014([], [], []).
    booksList_2009_2014([Book|Books], L2009, L2014) :-
      booksList_2009_2014(Books, L2009_t, L2014_t), % note: construct the tail(s) first
      Book = book(_,_,date(_,Year)),
      (  Year == 2009
      -> L2009 = [Book|L2009_t], L2014 = L2014_t
      ;  Year == 2014
      -> L2009 = L2009_t, L2014 = [Book|L2014_t]
      ;  % did you forgot year 2013 ?
         L2009 = L2009_t, L2014 = L2014_t
      ).
    

    但是将数据硬编码到逻辑中是任何语言的错误方法,在 Prolog 中更是如此。无论您现在的任务多么简单,更好的方法是避免将变量绑定到特定年份的书籍。保持声明性:

    books_of_year(AllBooks, Year, Result) :-
      Book = book(_,_,date(_,Year)),
      findall(Book, member(Book, AllBooks), Result).
    

    现在

    ?- setbooks(L), books_of_year(L,2009,Books2009), books_of_year(L,2014,Books2014).
    % L = ... not interesting
    Books2009 = [book('Name 4', 'Publisher ABC', date(5, 2009)), book('Name 3', 'Publisher ABC', date(2, 2009)), book('Name 1', 'Publisher ABC', date(6, 2009))],
    Books2014 = [book('Name 5', 'Publisher ABC', date(12, 2014)), book('Name 2', 'Publisher ABC', date(12, 2014))].
    

    【讨论】:

    • 你能帮忙吗?使用books_of_year 时,出现错误The free variable in 'findall' can only be used inside 'findall' Book。无法理解原因。
    • @Aliaxander:也许你应该发布一个关于你的问题的问题。我不清楚细节,因为我以前从未见过这样的警告......
    【解决方案2】:

    规则

    booksList_2009_2014([H|T],Books2009, Books2014):-
    booksList_2009_2014(T,Books2009, Books2014).  
    

    一次又一次地剥书,直到列表中有一项时您达到规则

    booksList_2009_2014([book(Name,Publisher,date(M1,Y1))],Books2009, Books2014):-
        Y1=2014 -> insertAtEnd(book(Name,Publisher,date(M1,Y1)),_,Books2014);
        Y1=2009 -> insertAtEnd(book(Name,Publisher,date(M1,Y1)),_,Books2009).
    

    这当然与列表中的最后一本书统一,因为它不断地从头上剥落。然后,由于您没有在第一个前面的规则中使用 head 元素进行调用,因此它将 Books2009 与对 insertAtEnd 的 one 调用统一起来,因为年份是 2009 年。

    你需要

    • 做一些事情用你剥离的 head 元素(也许做一个不同的谓词,只需要一本书)
    • 可能会制作一个蓄能器来传递您 2009 年和 2014 年的书籍

    【讨论】:

    • 累加器很有用,但当您需要将信息传递给递归调用时更是如此。这里似乎没有必要。
    • 在@Boris 评论中添加:累加器会使 OP' 代码(已经很复杂)变得更加复杂,并且会颠倒顺序,给这样的基本任务增加更多复杂性......
    【解决方案3】:

    因此,您尝试从满足两个条件之一的输入列表元素中进行选择,并将它们放入两个结果列表之一中:

    % list_c1_c2_r1_r2(List, Condition1, Condition2, Result1, Result2)
    

    基本情况:您的输入列表为空;所以结果也是空的。条件可以忽略。

    list_c1_c2_r1_r2([], _, _, [], []).
    

    列表的头部满足条件1:

    list_c1_c2_r1_r2([X|Xs], C1, C2, [X|Ys], Zs) :-
        satisfies(X, C1),
        list_c1_c2_r1_r2(Xs, C1, C2, Ys, Zs).
    

    列表的头部满足条件2:

    list_c1_c2_r1_r2([X|Xs], C1, C2, Ys, [X|Zs]) :-
        satisfies(X, C2),
        list_c1_c2_r1_r2(Xs, C1, C2, Ys, Zs).
    

    头部不满足两个条件中的任何一个:

    list_c1_c2_r1_r2([X|Xs], C1, C2, Ys, Zs) :-
        \+ satisfies(X, C1),
        \+ satisfies(X, C2),
        list_c1_c2_r1_r2(Xs, C1, C2, Ys, Zs).
    

    对于您的情况,您可以写 satisfies(X, Condition) 例如:

    satisfies(book(_, _, date(_, Year)), Year).
    

    所以,你可以查询:

    ?- books(Books), list_c1_c2_r1_r2(Books, 2009, 2014, Book2009, Books2014).
    Books = [book('Name 5', 'Publisher ABC', date(12, 2014)),
             book('Name 4', 'Publisher ABC', date(5, 2009)),
             book('Name 3', 'Publisher ABC', date(2, 2009)),
             book('Name 6', 'Publisher ABC', date(3, 2013)),
             book('Name 2', 'Publisher ABC', date(12, 2014)),
             book('Name 1', 'Publisher ABC', date(6, 2009))],
    Book2009  = [book('Name 4', 'Publisher ABC', date(5, 2009)),
                 book('Name 3', 'Publisher ABC', date(2, 2009)),
                 book('Name 1', 'Publisher ABC', date(6, 2009))],
    Books2014 = [book('Name 5', 'Publisher ABC', date(12, 2014)),
                 book('Name 2', 'Publisher ABC', date(12, 2014))] .
    

    您能否更改此设置以在不互斥的条件下正常工作?如果您跳过最后一个子句中的两个条件都不满足的要求,会发生什么?如何参数化评估条件的方式?如何参数化条件和结果列表的数量?

    【讨论】:

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