【问题标题】:Defining a rule that the user cannot query定义用户无法查询的规则
【发布时间】:2011-02-25 00:48:16
【问题描述】:

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

例如:

rule1():- rule2().

rule2():- 1

?-rule1().

是的

?-rule2().

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

【问题讨论】:

  • 您使用的是哪个 Prolog 系统?为什么用户不调用此特定规则(而不是不记录它)如此重要?
  • 可以说 rule2() 更新一个值。我只希望在您调用 rule1() 时更新该值。我不希望用户能够调用 rule2,因为它会搞砸一切。
  • 你尝试过使用模块吗?
  • @Kaarel:请解释一下。

标签: prolog logtalk


【解决方案1】:

使用 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/

【讨论】:

  • +1 来自我,但你能举一个(最小/琐碎)的例子吗?我从未见过/听说过 Logtalk
【解决方案2】:

首先,一些注意事项:

  • 我认为您的意思是“谓词”而不是“规则”。谓词是 name/k 事物,例如 help/0help/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/0 的第一个子句,我不使用等效的predicate2("Identification: 2349860293587"),因为我不确定子句的头部可能出现在Prolog 消息中的什么位置,而您不希望这样。我在第二个子句的末尾使用了fail,以便Prolog 在错误消息之后打印false 而不是true。最后,我不知道如何防止用户使用listing(predicate2) 查找源代码,这样如果他/她真的想要,仍然可以简单地查找正确的识别码。如果只是为了防止用户造成意外伤害,那么它应该足以作为一种保护。

【讨论】:

    【解决方案3】:

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

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

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

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

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

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

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

    最好的问候

    P.S.:这里已经解释了堆栈消除优化: [4] 沃伦,D.H.D. (1983):A Abstract Prolog Instruction Set,技术说明 309,SRI International,1983 年 10 月

    关于 Jekejeke Prolog 的讨论可在此处找到: http://www.jekejeke.ch/idatab/doclet/prod/en/docs/10_pro08/13_press/03_bench/05_optimizations/03_stack.html

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-15
      • 2016-02-03
      • 1970-01-01
      • 2020-09-30
      • 2017-09-05
      相关资源
      最近更新 更多