【问题标题】:Avoid double recursive call in Prolog避免 Prolog 中的双重递归调用
【发布时间】:2020-05-28 21:36:34
【问题描述】:

我有这个 Prolog 代码:

f([ ],-1).

f([H|T],S):- f(T,S1), S1>0, !, S is S1+H.

f([_|T],S):- f(T,S1), S is S1.

如何避免第二次(冗余)递归调用f(T,S1),在第三个子句中,谓词的整体效果保持不变?

我知道这可以通过定义一个额外的谓词来完成。

如何定义这样的谓词?

【问题讨论】:

  • 请指出几个使用这个谓词的例子。这可能会对您有所帮助。

标签: recursion prolog program-transformation


【解决方案1】:
| ?- length(L,_), f(L,R).
   L = [],
   R = -1
;  L = [_A],
   R = -1
;  L = [_A,_B],
   R = -1
;  L = [_A,_B,_C],
   R = -1
;  L = [_A,_B,_C,_D],
   R = -1
;  L = [_A,_B,_C,_D,_E],
   R = -1
;  L = [_A,_B,_C,_D,_E,_F],
   R = -1
;  L = [_A,_B,_C,_D,_E,_F,_G],
   R = -1
;  L = [_A,_B,_C,_D,_E,_F,_G,_H],
   R = -1
;  L = [_A,_B,_C,_D,_E,_F,_G,_H,_I],
   R = -1
;  L = [_A,_B,_C,_D,_E,_F,_G,_H,_I,_J],
   R = -1 ...

这会响铃吗?

好吧,这就是我的回应:

f(L, -1) :-
   length(L, _).

虽然这对于 f(L, 0) 现在终止并失败,而原始定义循环。如果您也坚持这种等效性:

f(L, MinusOne) :-
   length(L, _),
   MinusOne = -1.

【讨论】:

  • 是的,这个定义没有多大意义,但这仍然不是问题......
【解决方案2】:

首先我们重新编写它以更好地查看相似之处,

f([ ],-1).
f([H|T],S) :- f(T,S1), S1>0, !, S is S1+H.
f([H|T],S) :- f(T,S1),          S is S1.

接下来我们分解出相等的部分,并用新的谓词替换它的延续,该谓词将执行与之前相同的操作,并使用与之前涉及的相同逻辑变量:

f([ ],-1).
f([H|T],S) :- f(T,S1), additional_predicate(S1,S,H).

现在剩下要做的就是用新名称写下相同的目标:

additional_predicate(S1,S,H):- S1>0, !, S is S1+H.
additional_predicate(S1,S,H):- S is S1.

就是这样。

简单来说,在调用f(T,S1) 之后,S1 已经计算好了,所以不需要重新计算。

【讨论】:

  • ?什么时候会用到additional_predicate/3 的第一个子句?
  • 我读到的问题是关于谓词的转换,而不是关于它的含义。
  • 但即便如此,第一个子句也没用
  • 我展示了一个句法转换,所以它可以用作一个模式,在其他类似的情况下。消除死代码片段是一种语义转换。
  • 您的转换依赖于您未说明的语义假设,即剪切对f(T,S1) 没有影响。所以你和我一样语义化。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-04-08
  • 2017-06-16
  • 1970-01-01
  • 2021-09-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多