Prolog - SWI:如何创建一个用于计算给定值的结构

时间:2013-03-21 18:36:31

标签: inheritance prolog

我是prolog的第一次使用者,我已经使用它大约一个星期了,从{​​{3}}学习各种基础知识,这对学位有所帮助。我一直在设计一个虚构游戏标题的数据库查找,用户可以通过该数据查询游戏以获得游戏中给定角色的统计数据。如果你问我,听起来像一个很棒的项目:)

我一直在努力学习“框架”来描述我的节点以及如何使用它们,所以我将使用的格式将是这些线。我一直在学习的最新项目是prolog中的“Numbers”以及它们是如何工作的,在教程中看起来非常简单,直到我想以更动态的方式使用它。

我为我的数据节点设置了以下内容。 (伪代码)

框架:名称(字符),父(无),base_stat(8.0)。

框架:名称(npc),父(字符)。

框架:名称(敌人),父母(npc)。

框架:名称(red_faction),父级(敌人),atk_bonus(1.5),spd_bonus(1.25),def_bonus(0.25)。

框架:名称(blue_faction),父级(敌人),atk_bonus(1),spd_bonus(1),def_bonus(1)。

框架:名称(fire_warrior),父级(red_faction),级别(1.0),持有(无)。

框架:名称(water_consort),父级(blue_faction),级别(1.0),持有(无)。

咨询时我想向用户返回以下统计信息:攻击(?),速度(?),防御(?)。

我希望用一个等式来计算“?”我的程序的区域,将使用以下等式“(base_stat + level)* stat_bonus”。所以像这样: -

compute(attribute) :-
    stat_bonus(stat bonus from parent node for required stat example:atk_bonus)
    base_stat(value from the character node's base_stat)
    level(value from the character we are looking up: eg water_consort lvl1)
    attribute is (level+base_stat) * stat_bonus

在教师简要解释了prolog中数字的代数方法之后,我想出了这个结构。例子有: -

student(jack, 25).
student(jill, 21).

avg_Score(Z) :-
    student(jack, X),
    student(jill, Y),
    Z = (X + Y)/2.

我知道我需要提取X和Y的值,如果我想重建这些值,我将不得不重复该提取以使用它们。

现在回到我的小项目,我有一些派系节点(完整的图表有3个,但是没有必要超过两个,因为我假设任何需要的过程都可以应用N次),怎么做我得到了计算的信息?

假设我要为火战士请求数据,我的系统想要计算一个统计数据(比如说攻击),它会参考我所拥有的等式。我需要以下信息:

  1. 父节点“red_faction”
  2. 给出的攻击加值
  3. g.g.grandparent节点“character”
  4. 给出的base_stat
  5. 实例节点的级别
  6. 我的导师给我的例子只有在我要求所有内容的硬编码时才会起作用并且非常有限。有没有一种方法可以用于“N”派系和每个派系的“M”字符实例?我会再次打扰她,但直到复活节之后她都没有自由,而且我希望在周末的某个时候完成这个。从我可以告诉Prolog做的继承很好,但我不知道如何解决这个问题。

    提前感谢您提供任何建设性的意见。

1 个答案:

答案 0 :(得分:3)

欢迎来到Prolog!我很高兴你给它一个机会,我希望你发现它能很好地满足你的想象力,因为你似乎有很多!

你在Prolog中使用了很多奇怪的术语。我不清楚你的意思是“框架”或“节点”或“提取”,我很确定你正在使用“咨询”。我不知道这是不是因为教程是为SICStus编写的,或者只是通常的混乱。我通常也不会想到在Prolog中做继承,但我觉得这部分我确实理解。所以我希望你能继续玩,我们会看看我是否能够回答你的问题。

首先要做的事情。这不是什么大问题,但平均分应该像这样实施:

avg_score(Z) :-
  student(jack, X),
  student(jill, Y),
  Z is (X + Y) / 2.   % <-- note the use of 'is' instead of '='

这是一个常见的初学者错误。 Prolog默认不评估代数,所以当你说(X + Y) / 2之类的东西时,你所做的只是构建一个表达式,与div(plus(X, Y), 2)没有区别。实际上,唯一的区别是is知道如何评估像(X + Y) / 2而不是div(plus(X, Y), 2)这样的代数表达式。 (我会预先警告你,如果你从一开始就使用clpfd,你会发现Prolog会更有意义,并使用#=代替is。)

不,当然,avg_scorejackjill的嵌入是无用的。有一天,你可能想要计算所有学生的平均分数。首先让我们计算平均值:

average(L, Average) :-
  sum(L, Sum),
  length(L, Length),
  Average is Sum / Length.

% this is overly complex because I wanted to show you 
% the tail recursive version
sum(L, Sum) :- sum(L, 0, Sum).

sum([], Sum, Sum).
sum([X|Xs], Acc, Sum) :- Acc1 is X + Acc, sum(Xs, Acc1, Sum).

现在我们可以得到所有学生的平均成绩:

avg_score(Z) :-
  findall(X, student(_, X), Scores),
  average(Scores, Z).

我不知道这是否与您所询问的内容有关,但似乎它可能会有所帮助。

当然,另一种可能性是参数化。我们可以采用原始代码并由学生参数化:

avg_score(Student1, Student2, Z) :-
  student(Student1, X),
  student(Student2, Y),
  Z is (X + Y) / 2.

这似乎更有助于查询动物。而不是问attack(X)你会问attack(fire_warrior, X)

当你对Prolog这么新的时候,我讨厌把你送到Logtalk,但我怀疑它可能会包含你正在寻找的一些答案。它特别擅长以一种香草Prolog可能不具备的方式处理继承。但它可能是一个很大的转移。例如,您可以处理继承链以查询攻击统计信息,如下所示:

% this is the inheritance tree
parent_class(character, base).
parent_class(npc, character).
parent_class(enemy, npc).
parent_class(red_faction, enemy).
parent_class(blue_faction, enemy).
parent_class(fire_warrior, red_faction).
parent_class(water_consort, blue_faction).

% these are the attack bonuses
attack_bonus(base, 0).
attack_bonus(red_faction, 1.5).
attack_bonus(blue_faction, 1).

calc_attack_bonus(X, Bonus) :-
  attack_bonus(X, Bonus), !.
calc_attack_bonus(X, Bonus) :-
  \+ attack_bonus(X, Bonus),
  parent_class(X, Parent),
  calc_attack_bonus(Parent, Bonus).

这似乎适用于一些基本查询:

?- calc_attack_bonus(fire_warrior, Bonus).
Bonus = 1.5.

?- calc_attack_bonus(character, Bonus).
Bonus = 0.

我不知道你是否想要这种行为:

?- calc_attack_bonus(tree, Bonus).
false.

如果没有(如果parent_class失败,则很容易修复,将奖金统一为0,否则重复)。

然而,这并非所有可扩展的。更可扩展的方法可能是这样的:

% these are the attack bonuses
attribute(attack_bonus, base, 0).
attribute(attack_bonus, red_faction, 1.5).
attribute(attack_bonus, blue_faction, 1).

% base stats
attribute(base_stat, character, 8.0).

attribute(level, fire_warrior, 1.0).

现在我们可以毫不费力地编写我们需要的规则:

calc_attribute(X, Attribute, Value) :-
  attribute(X, Attribute, Value), !.
calc_attribute(X, Attribute, Value) :-
  \+ attribute(X, Attribute, Value),
  parent_class(X, Parent),
  calc_attribute(Parent, Attribute, Value).

现在攻击变得容易了:

attack(X, Value) :-
  calc_attribute(X, attack_bonus, Bonus),
  calc_attribute(X, base_stat, Base),
  calc_attribute(X, level, Level),
  Value is (Level + Base) * Bonus.

我们需要更多地概括一下,以便找到我们可以写compute的位置,因为理想情况下,我们想要说compute(fire_warrior, attack, Value)之类的内容。要做到这一点,我们需要知道attack_bonusattack有关,我们不知道。让我们重新构建attribute

% these are the attack bonuses
attribute(base,         bonus(attack), 0).
attribute(red_faction,  bonus(attack), 1.5).
attribute(blue_faction, bonus(attack), 1).

% base stats
attribute(character, base_stat, 8.0).

attribute(fire_warrior, level, 1.0).

现在我们正在做饭:

compute(X, Attribute, Value) :-
  calc_attribute(X, bonus(Attribute), Bonus),
  calc_attribute(X, base_stat, Base),
  calc_attribute(X, level, Level),
  Value is (Level + Base) * Bonus.

看哪:

?- compute(fire_warrior, attack, Value).
Value = 13.5.

我希望这就是你想要的。 :)

大编辑

为了好玩,我想我会看到使用Logtalk这对于Prolog的面向对象扩展语言会是什么样子。我是非常对Logtalk的新手,所以这可能是也可能不是一个好的方法,但它确实“做了伎俩”所以让我们看看它是否更符合你的想法。首先,基础对象:

:- object(base).

  :- public(base_stat/1).
  :- public(attack/1).
  :- public(bonus/2).
  :- public(level/1).
  :- public(compute/2).

  bonus(attack, 0).
  base_stat(0).
  level(0).

  compute(Attribute, Value) :-
      ::bonus(Attribute, Bonus),
      ::base_stat(Base),
      ::level(Level),
      Value is (Level + Base) * Bonus.

:- end_object.

这基本上定义了我们将要存储的关于每件事的事实,以及如何计算您感兴趣的属性。接下来我们建立对象层次结构:

:- object(character, extends(base)).
    base_stat(8.0).
:- end_object.

:- object(npc, extends(character)).
:- end_object.

:- object(enemy, extends(npc)).
:- end_object.

:- object(red_faction, extends(enemy)).
    bonus(attack, 1.5).
    bonus(speed, 1.25).
    bonus(defense, 0.25).
:- end_object.

:- object(blue_faction, extends(enemy)).
    bonus(attack, 1).
    bonus(speed, 1).
    bonus(defense, 1).
:- end_object.

:- object(fire_warrior, extends(red_faction)).
    level(1.0).
    holding(nothing).
:- end_object.

:- object(water_consort, extends(blue_faction)).
    level(1.0).
    holding(nothing).
:- end_object.

现在使用它非常简单:

?- fire_warrior::compute(attack, X).
X = 13.5.

?- water_consort::compute(attack, X).
X = 9.0.

我希望这有帮助!