在大学里做了一些Prolog并做了一些练习后,我决定更进一步,尽管我承认我对递归理解不太好,但我明白了这个概念和想法,但如何编码它仍然是我的一个问题。这就是为什么我很好奇是否有人知道如何帮助解决这个问题。
这个想法是给定一个数字,例如45,检查是否有可能制作一个从1开始的列表,将n+1放入列表中,以及列表的总和是否与给定的数字相同。
因此,对于45,[1,2,3,4,5,6,7,8,9]
是正确的。
到目前为止,我尝试查看Prolog中实现的[sum_list/2][1]
,但它只检查列表是否与其后面的数字相同。
因此,给定谓词lijstSom(L,S)
(荷兰语为listSum
),给定
?- lijstSom(L, 45)
L = [1,2,3,4,5,6,7,8,9];
False
我的想法是这样的,例如,如果S=45,做数字的步数(增加1)并减去S,如果0是余数,则返回列表,否则返回false
。
但为此,你需要计数器,我发现在递归中很难理解这一点。
编辑:
递归中的步骤。
基本情况空列表,0(计数器nr,即减去S),45(S,余数)
[1], 1, 44
[1,2], 2, 42
[1,2,3], 3, 39
我不知道如何阅读示例
?- lijstSom(L, 45)
L = [1,2,3,4,5,6,7,8,9],
False
但可以将谓词lijstSom(List, Sum)
看作是将某些整数列表与其和相关联,而不是计算整数列表的和。为什么是"特定列表"?因为我们有一个约束,即整数列表中的整数必须以1为增量单调递增,从1开始。
因此,您可以向Prolog处理器询问以下内容:
"谈谈lijstSom/2
的第一个自变量和lijstSom/2
的第二个自变量之间的关系(假设第一个是单调递增的整数列表,第二个是整数):
lijstSom([1,2,3], Sum)
应该返回true(因为是的,至少有一个解决方案),给出Sum
=6吗(因为它也构造了解决方案……我们在这里是构造主义的一个角落。
lijstSom(L, 6)
应该返回true(因为是的,至少有一个解决方案),并且给出解决方案[1,2,3]
。
lijstSom([1,2,3], 6)
应返回true(因为是的,[1,2,3]
有一个和6);不需要进一步的信息。
lijstSom(L, S)
应该是真的无穷级数和解对("生成解")。
L = [1], S = 1;
L = [1,2], S = 3;
L = [1,2,3], S = 6;
...
lijstSom([1,2,3], 7)
应返回false("fail"),因为7不在lijstSom
与[1,2,3]
的关系中,因为7=/=1+2+3。
人们甚至可能希望Prolog处理器能说出一些有趣的东西:
lijstSom([1,2,X], 6)
X = 3
甚至
lijstSom([1,2,X], S)
X = 3
S = 6
事实上,lijstSom/2
在数学上和物理上一样神奇,也就是说:
- 可以不受限制地访问列表的完整表<->和关系漂浮在柏拉图数学空间的某个地方
- 能够在不到无穷多的步骤中找到正确的条目
- 并输出
当然,由于非常实际的原因,我们被限制在低指数和有限数量的D可分辨符号的多项式算法中。糟透了!
因此,首先使用归纳定义定义lijstSom(L,S)
:
lijstSom([a list with final value N],S)
。。。如果。。。lijstSom([a list],S-N
和lijstSom([],0)
,因为空列表的总和为0
这很好,因为它提供了一个食谱,可以将任意长度的列表最终缩减为0大小的列表,同时保持其总和!
Prolog不擅长处理列表的尾部,但擅长处理头部,所以我们欺骗了&更改我们对lijstSom/2
的定义,声明列表以相反的顺序给出:
lijstSom([3,2,1], 6)
现在是一些代码。
#=
是库(clpfd)中的"常数等于"运算符。要使用它,我们需要首先发出use_module(library(clpfd)).
命令。
lijstSom([],0).
lijstSom([K|Rest],N) :- lijstSom([Rest],T), T+K #= N.
上面遵循了lijstSom
的数学要求,并允许Prolog处理器执行其计算:在第二个子句中,它可以从大小为a-1的列表的值计算大小为a的列表的数值,从列表长度总是递减的阶梯"下降",直到到达lijstSom([],0).
的终止情况。
但我们还没有说任何关于单调递减的列表。更准确地说:
lijstSom([],0) :- !.
lijstSom([1],1) :- ! .
lijstSom([K,V|Rest],N) :- K #= V+1, T+K #= N, lijstSom([V|Rest],T).
更好!
(我们还添加了"!",告诉Prolog处理器不要寻找超过这一点的替代解决方案,因为我们对算法的了解比以往任何时候都要多。此外,第三行可以工作,但这只是因为我在运行下面的测试并使其通过后就得到了它。)
如果检查失败,Prolog处理器将显示"false"-没有针对您输入的解决方案。这正是我们想要的。
但它有效吗?我们能在这个杰出的物理机器的"数学性"上走多远?
为约束加载library(clpfd)
,为单元测试使用library(plunit)
:
将其放入文件x.pl
中,您可以使用[x]
别名consult('x')
加载该文件,也可以在Prolog REPL:上使用make
重新加载该文件
:- use_module(library(clpfd)).
lijstSom([],0) :-
format("Hit case ([],0)n"),!.
lijstSom([1],1) :-
format("Hit case ([1],1)n"),!.
lijstSom([K,V|Rest],N) :-
format("Called with K=~w, V=~w, Rest=~w, N=~wn", [K,V,Rest,N]),
K #= V+1,
T+K #= N,
T #> 0, V #> 0, % needed to avoid infinite descent
lijstSom([V|Rest],T).
:- begin_tests(listsom).
test("0 verify") :- lijstSom([],0).
test("1 verify") :- lijstSom([1],1).
test("3 verify") :- lijstSom([2,1],3).
test("6 verify") :- lijstSom([3,2,1],6).
test("0 construct") :- lijstSom(L,0) , L = [].
test("1 construct") :- lijstSom(L,1) , L = [1].
test("3 construct") :- lijstSom(L,3) , L = [2,1].
test("6 construct") :- lijstSom(L,6) , L = [3,2,1].
test("0 sum") :- lijstSom([],S) , S = 0.
test("1 sum") :- lijstSom([1],S) , S = 1.
test("3 sum") :- lijstSom([2,1],S) , S = 3.
test("6 sum") :- lijstSom([3,2,1],S) , S = 6.
test("1 partial") :- lijstSom([X],1) , X = 1.
test("3 partial") :- lijstSom([X,1],3) , X = 2.
test("6 partial") :- lijstSom([X,2,1],6) , X = 3.
test("1 extreme partial") :- lijstSom([X],S) , X = 1, S = 1.
test("3 extreme partial") :- lijstSom([X,1],S) , X = 2, S = 3.
test("6 extreme partial") :- lijstSom([X,2,1],S) , X = 3, S = 6.
test("6 partial list") :- lijstSom([X|L],6) , X = 3, L = [2,1].
% Important to test the NOPES
test("bad list", fail) :- lijstSom([3,1],_).
test("bad sum", fail) :- lijstSom([3,2,1],5).
test("reversed list", fail) :- lijstSom([1,2,3],6).
test("infinite descent from 2", fail) :- lijstSom(_,2).
test("infinite descent from 9", fail) :- lijstSom(_,9).
:- end_tests(listsom).
然后
?- run_tests(listsom).
% PL-Unit: listsom ...................... done
% All 22 tests passed
Dijkstra会怎么说?是的,他可能会抱怨什么。