这是我想要了解的代码。
co(X) :- co(X,[],L).
co([],A,A):- write(A).
co([X|Xs], A, L) :- p(X-Z,A,R), !, Z1 is Z+1, co(Xs, [X-Z1|R], L).
co([X|Xs], A, L) :- co(Xs, [X-1|A], L).
p(X-Y,[X-Y|R],R):- !.
p(X,[H|Y], [H|Z]) :- p(X,Y,Z).
'!'有什么用?和上面代码中的谓词p(,,)。或者任何人都可以在上面的代码的每一步添加注释,以便我能够理解。感谢。
答案 0 :(得分:3)
初学者倾向于使用!/0
因为他们不知道其负面后果。
这是因为在初学者中流行的大多数Prolog教科书非常糟糕,并且经常包含有关!/0
的错误和误导性信息。
@false有excellent answer何时使用!/0
。总结:不。
相反,专注于关于什么保持的声明性描述,并尝试使用纯粹和单调的方法(如约束,干净的表示,...)来使描述变得优雅和通用。
答案 1 :(得分:3)
您的计划中有很多事情需要解决。削减甚至不是主要问题。请bring me the broom。
您接下来的精确界面是什么?目前co(Xs)
的目的是产生副作用。否则它对于给定列表可能成功或失败。但不仅仅是那个。然而,这种副作用根本不需要 - 并且对于大多数情况来说并不是一种有用的方法,因为这样的程序实际上是不可用的并且无视任何逻辑推理。你需要留下一个洞让一些结果潜伏在关系之外。添加另一个参数并移除write/1
中的目标co/3
。
co(Xs, D) :-
co(Xs, [], D).
现在,您可以单独使用顶级shell测试程序。您不需要任何线束或沙箱来检查"输出"。它就在那里,很容易在一个单独的论证中。
接下来是co/3
本身。在这里,最好的方法是通过分离一些问题来澄清意图,并使这些额外的论点更具意图揭示。 D
代表词典。另一个好名称是KVs
意义列表(复数s
)的键值对。请注意不同状态的编号方式:它们以D0
,D1
,...开头,最后有D
。通过这种方式,如果您开始编写规则,则可以将D0,D
置于脑中而不知道该规则中需要多少个状态。
co([], D,D).
co([X|Xs], D0,D) :-
nn(X, D0,D1),
co(Xs, D1,D).
nn(K, D0,D) :-
p(K-V0,D0,D1), !,
V is V0+1,
D = [X-V|D1].
nn(K, D0,D) :-
D = [K-1|D0].
p(X-Y,[X-Y|R],R):- !.
p(X,[H|Y], [H|Z]) :- p(X,Y,Z).
co/3
现在更清楚地揭示了它的意图。它以某种方式将列表的元素与某些状态联系起来,即更新"对于每个元素。有一句话:这是左撇子。甚至有一个谓词:foldl/4
。因此,我们可以将co/3
定义为:
co(Xs, D0,D) :-
foldl(nn, Xs, D0,D).
或者更好地完全摆脱co/3
:
co(Xs, D) :-
foldl(nn, Xs, [], D).
foldl(_C_3, [], S,S).
foldl(C_3, [X|Xs], S0,S) :-
call(C_3, X, S0,S1),
foldl(C_3, Xs, S1,S).
请注意,到目前为止,我甚至没有触及你的任何削减,这些现在是他们的最后时刻......
p/3
中的切入不起任何作用。无论如何,在目标p/3
之后立即进行切割。然后,X-Y
中不需要p/3
,您可以安全地将其替换为另一个变量。简而言之,p/3
现在是Prolog序言中的谓词select/3
。
select(E, [E|Xs], Xs).
select(E, [X|Xs], [X|Ys]) :-
select(E, Xs, Ys).
nn(K, D0,D) :-
select(K-V0, D0,D1), !,
V is V0+1,
D = [K-V|D1].
nn(K, D0,D) :-
D = [K-1|D0].
无法轻易删除这一个剩余的剪辑:它可以保护替代子句在K-V
中不会出现D
时使用。但是,还有更好的方法来表达这一点。
(\+)/1
nn(K, D0,D) :-
select(K-V0, D0,D1),
V is V0+1,
D = [K-V|D1].
nn(K, D0,D) :-
\+select(K-_, D0,_),
D = [K-1|D0].
现在,每条规则都规定了自己想要的东西。这意味着,我们现在可以自由地改变这些规则的顺序。称之为迷信,但我更喜欢:
nn(K, D0,D) :-
\+select(K-_, D0,_),
D = [K-1|D0].
nn(K, D0,D) :-
select(K-V0, D0,D1),
V is V0+1,
D = [K-V|D1].
dif/2
为了使这成为一种真正的关系,我们需要摆脱这种否定。而不是说,没有解决方案,我们可以要求所有密钥(密钥是Key-Value
中的第一个参数)与K
不同。
nokey(_K, []).
nokey(K, [Kx-|KVs]) :-
dif(K, Kx),
nokey(K, KVs).
nn(K, D,[K-1|D]) :-
nokey(K, D).
nn(K, D0,[K-V|D]) :-
select(K-V0, D0,D),
V is V0+1.
在lambdas的帮助下,nokey(K, D)
变为maplist(K+\(Kx-_)^dif(Kx,K), D)
总结一下,我们现在有:
co(Xs, D) :-
foldl(nn, Xs, [], D).
nn(K, D,[K-1|D]) :-
maplist(K+\(Kx-_)^dif(Kx,K), D).
nn(K, D0,[K-V|D]) :-
select(K-V0, D0,D),
V is V0+1.
那么这个关系是什么:第一个参数是一个列表,第二个参数是一个键值列表,每个元素和列表中出现的次数。