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

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

问题描述

我经常在 Prolog 中编写代码,其中涉及一些算术计算(或整个程序中重要的状态信息),首先获取存储在谓词中的值,然后重新计算值,最后使用 retractallassert 因为在 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.

附:我刚开始学习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?

推荐答案

避免这种情况的一般方法是考虑计算的状态之间的关系:你使用一个参数来保存与您的程序相关的状态 before 计算,第二个参数描述状态 after 一些计算.例如,要描述对值 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和retractall来实现全局(或状态)变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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