定义用户无法查询的规则

问题描述 投票:0回答:3

如何定义用户无法查询的规则? 我只希望程序本身通过另一个规则来调用这个规则。

例如:

规则1():-规则2()。

规则2():- 1<5.

?-规则1()。

真实

?-rule2()。

(我不知道答案是什么,我只是希望这个查询失败!)

prolog logtalk
3个回答
5
投票

使用 Logtalk 对象来封装您的谓词。只有声明为 public 的谓词才能被调用(从对象外部)。 Prolog 模块不会阻止调用任何谓词,因为使用显式限定会绕过显式导出的谓词列表。

一个简单的例子:

:- object(rules).

    :- public(rule1/1).
    rule1(X) :-
        rule2(X).

    rule2(X) :-
        X < 5.

:- end_object.

编译并加载上面的对象后:

?- rules::rule1(3).
true.

?- rules::rule2(3).
error(existence_error(predicate_declaration,rule2(3)),rules::rule2(3),user)

如果您编辑目标代码并显式将rule2/1声明为私有,您将得到错误:

?- rules::rule2(3).
error(permission_error(access,private_predicate,rule2(3)),rules::rule2(3),user)

更多信息和大量示例请访问 http://logtalk.org/


2
投票

首先,一些注意事项:

  • 我认为你的意思是“谓词”而不是“规则”。谓词是一个

    name/k
    事物,例如
    help/0
    (而
    help/1
    是另一个),可以有多个子句,其中包括事实和规则,例如
    length([], 0).
    (事实)和
    length([H|T], L) :- ... .
    (规则)是一个谓词
    length/2
    的两个子句。

  • 不要对没有参数的谓词使用空括号——至少在 SWI-Prolog 中,这根本不起作用。只需在所有地方使用

    predicate2
    代替
    predicate2()

  • 如果您尝试调用未定义的谓词,SWI-Prolog 会说

    ERROR: toplevel: Undefined procedure: predicate2/0 (DWIM could not correct goal)
    而 Sicstus-Prolog 会说
    {EXISTENCE ERROR: predicate2: procedure user:predicate2/0 does not exist}

现在,给出答案。我想到了两个想法。

(1) 这是一个 hack,但您可以在每次需要谓词时断言它们并在之后立即收回它们:

predicate1 :- 
      assert(predicate2), predicate2, retractall(predicate2).

如果您想要

predicate2
的正文和参数,请执行
assert(predicate2(argument1, argument2) :- (clause1, clause2, clause3))

(2) 实现此目的的另一种方法是为谓词引入一个额外的参数,您不希望用户调用该参数,并将其用于用户无法提供的标识,但您可以从您的调用谓词。这可能是一个看起来随机的大常数,甚至是一个句子。如果提供了错误的标识,您甚至可以输出自定义错误消息。

示例:

 predicate1 :- 
        predicate2("Identification: 2349860293587").

 predicate2(Identification) :- 
        Identification = "Identification: 2349860293587", 
        1 < 5.
 predicate2(Identification) :- Identification \= "Identification: 2349860293587",
        write("Error: this procedure cannot be called by the user. Use predicate1/0 instead."), 
        fail.

我没有对

predicate2("Identification: 2349860293587")
的第一个子句使用等效的
predicate2/0
,因为我不确定该子句的头部可能出现在 Prolog 消息中的位置,而您不希望这样。我在第二个子句的末尾使用
fail
,以便 Prolog 在错误消息后打印
false
而不是
true
。最后,我不知道如何阻止用户使用
listing(predicate2)
查找源代码,这样如果他/她确实想要的话,仍然可以简单地查找正确的识别码。如果只是为了防止用户造成意外伤害,那么作为一种保护就足够了。


1
投票

这让我想起了Java中的功能。那里可以查询到 当前的调用栈,并用它来调节调用的权限 一种方法。翻译成 Prolog 我们在旧的 DEC-10 Prolog 中发现 以下谓词:

祖先(L)
将 L 与当前子句的祖先目标列表统一。 该列表以父目标开始,以最近的目标结束 来自编译子句中的调用的祖先。名单已打印 使用 print 并且每个条目前面都有调用号 括号后跟深度数字(如在 跟踪消息)。如果调用没有数字(这将 如果调试模式直到进一步执行才打开,则会发生) 然后用“-”标记。不适用于编译代码。

由于顶层通常是已编译的谓词 prolog/0,因此这可能是 用于编写一个谓词来检查它自己的调用堆栈,然后决定 是否要投入使用。

rule2 :- ancestors(L), length(L,N), N<2, !, write('Don't call me'), fail.
rule2 :- 1<5.

在现代 Prolog 中,我们不再经常找到祖先/1 谓词。 但可以按照以下方式进行模拟。只需抛出一个错误,然后 如果错误带有堆栈跟踪,您将获得所需的一切:

ancestors(L) :- catch(sys_throw_error(ignore),error(ignore,L),true).

但要注意堆栈消除优化可能会减少堆栈,从而 祖先/1 返回的列表。

致以诚挚的问候

P.S.:堆栈消除优化已经在这里解释过: [4] 沃伦,D.H.D. (1983):抽象 Prolog 指令集,技术说明 309,SRI International,1983 年 10 月

© www.soinside.com 2019 - 2024. All rights reserved.