如何避免在Prolog中使用assert和tractallall来实现全局(或状态)变量 [英] How to avoid using assert and retractall in Prolog to implement global (or state) variables

查看:207
本文介绍了如何避免在Prolog中使用assert和tractallall来实现全局(或状态)变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我经常最终用Prolog编写代码,其中涉及一些算术计算(或整个程序中重要的状态信息),方法是首先获取谓词中存储的值,然后重新计算该值,最后使用assert,因为在Prolog中,我们不能使用is两次为变量赋值(因此,几乎每个需要修改的变量都是全局变量).我知道这不是Prolog中的好习惯.在这方面,我想问:

I often end up writing code in Prolog which involves some arithmetic calculation (or state information important throughout the program), by means of first obtaining the value stored in a predicate, then recalculating the value and finally storing the value using retractall and assert because in Prolog we cannot assign values to variable twice using is (thus making almost every variable that needs modification, global). I have come to know that this is not a good practice in Prolog. In this regard I would like to ask:

  1. 为什么在Prolog中这是一个不好的做法(尽管我本人不喜欢为了拥有一个灵活的(可修改的)变量而经历上述步骤)?

  1. Why is it a bad practice in Prolog (though i myself don't like to go through the above mentioned steps just to have have a kind of flexible (modifiable) variable)?

避免这种做法的一般方法有哪些?小例子将不胜感激.

What are some general ways to avoid this practice? Small examples will be greatly appreciated.

P.S.我刚刚开始学习Prolog.我确实有使用C之类的语言进行编程的经验.

P.S. I just started learning Prolog. I do have programming experience in languages like C.

下面是我想说的一个不好的例子(在win-prolog中):

A bad example (in win-prolog) of what I want to say is given below:

:- dynamic(value/1).
:- assert(value(0)).

adds :- 
   value(X),
   NewX is X + 4,
   retractall(value(_)),
   assert(value(NewX)).

mults :-
   value(Y),
   NewY is Y * 2,
   retractall(value(_)),
   assert(value(NewY)).

start :-
   retractall(value(_)),
   assert(value(3)),
   adds,
   mults,
   value(Q),
   write(Q).

然后我们可以像这样查询:

Then we can query like:

?- start.

在这里,这是非常琐碎的,但是在实际的程序和应用程序中,上面显示的全局变量方法不可避免.有时,上面给出的列表(如assert(value(0)) ...)会变得很长,并且带有用于定义更多变量的更多断言谓词.这样做是为了使不同功能之间的值通信成为可能,并在程序运行期间存储变量的状态.

Here, it is very trivial, but in real program and application, the above shown method of global variable becomes unavoidable. Sometimes the list given above like assert(value(0))... grows very long with many more assert predicates for defining more variables. This is done to make communication of the values between different functions possible and to store states of variables during the runtime of program.

最后,我想知道一件事: 尽管您为避免这种情况提出了各种解决方案,但上述做法何时仍不可避免?

Finally, I'd like to know one more thing: When does the practice mentioned above become unavoidable in spite of various solutions suggested by you to avoid it?

推荐答案

避免这种情况的一般方法是根据计算状态之间的关系进行思考:您可以使用一个参数来保存与您的程序在计算之前 有关的状态,以及第二个参数,描述在进行计算之后 的状态.例如,要描述对值V0的一系列算术运算,可以使用:

The general way to avoid this is to think in terms of relations between states of your computations: You use one argument to hold the state that is relevant to your program before a calculation, and a second argument that describes the state after some calculation. For example, to describe a sequence of arithmetic operations on a value V0, you can use:

state0_state(V0, V) :-
    operation1_result(V0, V1),
    operation2_result(V1, V2),
    operation3_result(V2, V).

请注意状态(在您的情况下为:算术值)如何通过谓词.命名约定V0-> V1-> ...-> V可以轻松扩展到任意数量的操作,并有助于记住V0是初始值,而V是该值在执行各种操作之后.每个需要访问或修改状态的谓词都有一个参数,可让您将状态传递给它.

Notice how the state (in your case: the arithmetic value) is threaded through the predicates. The naming convention V0 -> V1 -> ... -> V scales easily to any number of operations and helps to keep in mind that V0 is the initial value, and V is the value after the various operations have been applied. Each predicate that needs to access or modify the state will have an argument that allows you to pass it the state.

像这样通过状态进行线程化的一个巨大优势是,您可以轻松地独立地推断每个操作:您可以对其进行测试,调试,使用其他工具进行分析,例如,无需设置任何隐式全局状态.另一个巨大的好处是,如果您使用的是足够笼统的谓词,则可以在更多方向上使用程序.例如,您可以问:哪些初始值导致给定的结果?

A huge advantage of threading the state through like this is that you can easily reason about each operation in isolation: You can test it, debug it, analyze it with other tools etc., without having to set up any implicit global state. As another huge benefit, you can then use your programs in more directions provided you are using sufficiently general predicates. For example, you can ask: Which initial values lead to a given outcome?

?- state0_state(V0, given_outcome).

使用命令式样式时,这当然是不可能的.因此,您应该使用 constraints 而不是is/2,因为is/2仅在一个方向上起作用.约束更易于使用,并且是低级算术的更通用的现代替代方法.

This is of course not readily possible when using the imperative style. You should therefore use constraints instead of is/2, because is/2 only works in one direction. Constraints are much easier to use and a more general modern alternative to low-level arithmetic.

动态数据库也比通过变量进行线程化状态要慢,因为动态数据库对每个assertz/1执行索引等操作.

The dynamic database is also slower than threading states through in variables, because it performs indexing etc. on each assertz/1.

这篇关于如何避免在Prolog中使用assert和tractallall来实现全局(或状态)变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆