【问题标题】:Prolog add N to 0 to the listProlog 将 N 加到 0 到列表中
【发布时间】:2015-11-25 20:32:33
【问题描述】:

我想在结果列表中添加 N 到 0 个数字。

示例查询

?- add(5,R).

应该返回答案:

R = [5,4,3,2,1,0].

我已经尝试了以下代码,但没有成功。

add(0, 0).
add(N, [R]) :-
   N1 is N-1,
   add(N1, [R|N]).

【问题讨论】:

    标签: list prolog add


    【解决方案1】:

    你离得太近了!

    add(0, [0]).
    add(N, [N|R]) :- 
      N > 0, 
      N1 is N-1, 
      add(N1, R).
    

    那么,这里有什么不同?

    1. add(0, [0])[0] 而不是 0 因为你正在构建一个列表,而不是一个整数;否则你会得到看起来相当尴尬的 [5,4,3,2,1|0] 结果。

    2. N > 0 作为守卫,以确保我们不会在遇到基本情况后永远循环爬取负数。

    3. 这项工作是在add/2 的第二个子句的头部而不是它的主体中完成的。也就是说,我们的模式是add(N, [N|R]) 而不是add(N, [R])。这是因为该术语将 N 添加到列表的头部,而不是在重复之前添加它。

    4. 同样,[R|N] 中有一个简单的反转;这将建立一种倒退的列表。

    总而言之,我认为你非常接近。在提示下进行更多的实验可能足以修复它。您是否尝试过使用trace/0

    【讨论】:

    • 首先,感谢您的解释。我学得很好。我不知道追踪。我搜索了一段时间。这是非常有用的。再次感谢您。
    【解决方案2】:

    我们展示了this answer 的模拟(它处理来自0 的连续整数升序)。

    :- use_module(library(clpfd))。 :- set_prolog_flag(toplevel_print_anon, 假的)。

    基于equidistant_stride/2我们查询:

    ?- N = 10, Zs = [N|_Zs0], length(_Zs0, N), 等距_步幅(Zs, -1)。 N = 10,Zs = [10,9,8,7,6,5,4,3,2,1,0]。

    让我们重新运行1我们在this previous answer 中进行的运行时测量!

    ?- between(1,6,E), Nis10^E, garbage_collect, call_time(numlist(0, N, _), T1_in_ms), 垃圾收集, call_time((_Zs = [N|_Z], 长度(_Z, N), equidistant_stride(_Zs, -1)), T2_in_ms)。 N = 10,T1_in_ms = 0,T2_in_ms = 0 ; N = 100,T1_in_ms = 1,T2_in_ms = 0 ; N = 1000,T1_in_ms = 1,T2_in_ms = 1 ; N = 10000,T1_in_ms = 3,T2_in_ms = 12 ; N = 100000,T1_in_ms = 14,T2_in_ms = 32 ; N = 1000000,T1_in_ms = 90,T2_in_ms = 280。

    编辑

    此答案的过去修订无意中 sk(r)ewed 运行时测量以支持。如何? 很简单:reverse/2 的目标跟在 numlist/3 之后,尽管在这种情况下它是无用的。

    现在应该会更好:Thx 2 @JanWielemaker 4 报告!


    脚注 1: 使用 SWI-Prolog 版本 7.3.11(64 位)。

    【讨论】:

    • @JanWielemaker。现在好多了!看到这些小的二元谓词这样合作是不是很高兴?
    • 是和不是。通过添加reverse/2,您有点作弊。编写一个正确排序的 numlist/3 也同样容易。这就是为什么我省略了时序比较的反向。剩下的就是一个明显的算法和一个声明性规范之间的比较,我们只需要希望约束求解器足够聪明以找到正确的算法。给定一个复杂的搜索问题,首先尝试约束是明智的。然而,一个好的算法可预测的
    • @JanWielemaker。是的,有点:) 我不打算进行比较。将进行编辑以提高公平性。
    • @JanWielemaker。关于算法性能的可预测性,我持怀疑态度:(1)一般来说,是的,(2)但是,(3)具有不同的准确性,(4)抽象测量比测量运行时更好,因为(5)实际的 CPU 确实缓存、预测器、推测执行、分页虚拟内存等特技的海市蜃楼,以提高遗留代码的平均案例性能。
    • @JanWielemaker。 SWI 支持大页面怎么样?毕竟,TLB 是一种宝贵的资源。以及如何对 GC 进行 SIMD 化(比如标记)?既然已经摘到了低悬的果实,矢量收集/分散已经达到了主流 CPU。是我们使用它的时候了! 4 U c,我有很多绝妙的想法:)
    【解决方案3】:

    使用

    :- use_module(library(clpfd))。 :- set_prolog_flag(toplevel_print_anon, 假的)。

    我们这样定义n_to_0/2

    n_to_0(N,[Z|Zs]) :-
       length(Zs,N),
       [Z|Zs] ins 0..N,
       chain([Z|Zs],#>).
    

    OP 给出的示例查询:

    ?- n_to_0(5,Zs).
    Zs = [5,4,3,2,1,0].
    

    使用n_to_0/2 的最一般查询怎么样?

    ?- n_to_0(N,Zs)。 N = 0,Zs = [0] ; N = 1,Zs = [1,0] ; N = 2,Zs = [2,1,0] ; N = 3,Zs = [3,2,1,0] ; N = 4,Zs = [4,3,2,1,0] ; N = 5,Zs = [5,4,3,2,1,0] ; N = 6,Zs = [6,5,4,3,2,1,0] ...

    编辑

    @JanWielemaker 指出 n_to_0/2(如上定义)非常慢,尤其是在将其与非 clpfd 对应物进行比较时: 非常感谢您的报告! 自己看...

    ?- between(1, 3, E), Nis10^E, call_time((numlist(0, N, _Zs0), reverse(_Zs0, _)), T1_in_ms), call_time(n_to_0(N, _), T2_in_ms)。 E = 1,N = 10,T1_in_ms = 0,T2_in_ms = 1 ; E = 2,N = 100,T1_in_ms = 0,T2_in_ms = 104 ; E = 3,N = 1000,T1_in_ms = 0,T2_in_ms = 29701 ...

    查看this new, improved, clpfd-based answer

    【讨论】:

    • 是的,这也可以,但是老师不希望我们使用图书馆。
    • 它肯定是很好的声明性。它还使用专门的约束,这使其更像是一个 library 解决方案。而且,它真的。只需访问swish.swi-prolog.org/p/pjAHBXed.pl
    • 关于“库解决方案”的措辞:在我看来,提供一个至少不自动加载整数约束的 Prolog 系统是限制和有害的。 Stackoverflow 上的几乎所有 Prolog sn-ps 都将或已经确实受益于声明性整数算术。我希望 SWI 能够像 GNU Prolog 和 B-Prolog 一样工作,并且让 CLP(FD) 约束开箱即用。无论如何,与其他库谓词没有冲突,现在 SWI 终于像 SICStus 一样拥有transpose_ugraph/2,这是在 transpose/2 冲突首次报告并在 SICStus 中长期解决的 8 年后。
    • @mat。我赞同你的意见。当然!你还记得你的第一个 Prolog 查询吗?您曾经的第一个 Prolog 查询?我有,我猜你也有:?- stadt(X).(注意“Stadt”是德语“城镇”/“城市”的意思)。其他合适的“第一”查询包括 IMO ?- 1+1 #= 2.?- X #> 1, X #< 4.?- X #\= 0. 等等……但绝对不是 ?- use_module(library(clpfd)).
    猜你喜欢
    • 2018-04-25
    • 2015-02-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-08
    相关资源
    最近更新 更多