1

我是 prolog 的第一次用户,我已经使用它大约一周了,从这里学习各种基础知识,这在一定程度上有所帮助。我一直在为一个虚构的游戏标题设计一个数据库查找,用户可以在其中查阅动物寓言以获得游戏中给定角色的统计数据。如果你问我,这听起来像是一个很棒的项目:)

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

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

框架:名称(字符)、父级(无)、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

在一位导师简要解释了序言中数字的代数方法后,我提出了这个结构。有这样的例子: -

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. gggrandparent 节点“字符”给出的 base_stat
  3. 实例节点的级别

我的导师给我的例子只有在我对所有请求都进行硬编码并且非常有限的情况下才有效。有没有一种方法可以用于每个派系的“N”个派系和“M”个角色实例?我会再次打扰她,但她要等到复活节后才有空,我希望在周末的某个时候完成。据我所知,Prolog 的继承非常好,但我不知道如何解决这个问题。

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

4

1 回答 1

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) / 2but not div(plus(X, Y), 2)clpfd(我会预先警告您,如果您从头开始使用 Prolog并使用#=而不是. ,您会发现 Prolog 更有意义is。)

avg_score不,当然,没有什么帮助的是它jack并且jill嵌入其中。有一天,您可能想要计算所有学生的平均分数。首先让我们计算平均值:

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,但我怀疑它可能包含您正在寻找的一些答案。它特别擅长以 vanilla 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失败,将 Bonus 统一为 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_bonus是相关的attack,但我们不知道。让我们稍微重构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 会是什么样子,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.

我希望这有帮助!

于 2013-03-21T22:55:38.437 回答