- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
在我上次的考试中,我必须根据以下说明编写一个名为 double/2
的 Prolog 谓词:double(X, Y)
应该为 true if Y
是一个与 X
长度相同的整数列表,其中每个偶数的 X
都被替换为其 double 。因此,例如,查询 double([1, 3, 2, 4], X).
应该结果 X = [1, 3, 4, 8]。
为了简单起见,我被允许使用仿函数 even/1
,而无需定义它(实际上很容易定义它),当参数为偶数时为真,当参数为偶数时为假否则。实际上,我最终也使用仿函数 odd/1
编写了程序。但我的教授告诉我:“你可以只用偶数来写,没必要用奇数!”所以我想知道我怎么能这样写。
我写的是以下内容:
double([], []).
double([N|L], [N|D]) :- odd(N), !, double(L, D).
double([N|L], [M|D]) :- even(N), M is 2*N, double(L, D).
备注:如果我从最后一行代码中删除 even(N)
(所以如果我只使用 odd(N)
,这实际上与使用相同只有 even(N)
,因为我只使用其中一个),那么程序仍然可以工作。但这不是一个理想的解决方案,因为这样“剪切”变成了红色剪切(在我的程序中它是绿色剪切)。
最佳答案
如果您只是对正确的解决方案感兴趣,请采用@PauloMoura 的解决方案。这就是我认为这个练习的目的。以您的原始程序为例,(乍一看)似乎可以删除第二个子句中的目标 even(N)
。
但在此之前,让我明确一点,谓词名称 doubles/2
是用词不当。我宁愿说 list_semidoubled/2
...
odd(N) :- N mod 2 =:= 1.double([], []).double([N|L], [N|D]) :- odd(N), !, double(L, D).double([N|L], [M|D]) :- %even(N),M is 2*N, double(L, D).
However, the cut above does a little bit more than we expected. It does not cut when odd(N)
is true alone, but there is a tiny little extra condition that sneaked into our program. Do you see it? Let's have a look at the relevant part:
double([N|L], [N|D]) :-
odd(N), !, ...
有odd(N)
,但是看上面!在头脑中,另一种情况正在那里。它一直等到它可以造成严重破坏! “隐藏”的条件是:
If
N
is equal (unifies) to the first element in the second argument!
让我们试试吧!
?- double([1], Xs).
Xs = [1].
Worx 正如预期的那样,不是吗。然而:
?- double([1], [2]).
true.
天狮,这里发生了什么?那应该会失败!
以这种方式表现的谓词缺乏坚定性。我们预计查询会失败,因为 Prolog 没有向我们展示这个解决方案。
所以上面的剪裁并不总是像你预期的那样剪裁,但比你预期的要少一点。这些错误通常很难找到,因此最好从一开始就避免它们。您有多种选择:
这可能是初学者的最佳选择。而且它不一定效率较低。我宁愿:
double(Xs, Ys) :
maplist(n_semidoubled, Xs, Ys).
n_semidoubled(X, Y) :-
M is X mod 2,
( M = 0,
Y is X*2
; M = 1,
Y is X
).
这可以改进为 - 有点骇人听闻:
n_semidoubled(X, Y) :-
Y is X*(2-X mod 2).
(\+)/1
, once/1
, if-then-else@PaulMoura 已经向您展示了一种这样的可能性。另一种是使用 (\+)/1
:
n_semidoubled(X, X) :-
odd(X).
n_semidoubled(X, Y) :-
\+odd(X),
Y is X*2.
如果您现在仍决定使用此构造,请尝试重新构建您的程序,使剪切的范围尽可能地局部化。也就是说,与其将切割放在递归规则中,不如做一个局部谓词:
doubles([], []).
doubles([E|Es], [M|Ms]) :-
n_semidoubled(E, M),
doubles(Es, Ms).
n_semidoubled(E, M) :-
odd(E),
!,
M is E.
n_semidoubled(E, M) :-
M is E*2.
您的原始程序中还有另一个与剪辑问题无关的异常情况。考虑:
?- double([1*1],Xs).
Xs = [1*1].
关于序言程序 : another way to define a functor,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26938380/
我是一名优秀的程序员,十分优秀!