我知道这看起来有点傻,但我试图找到使用Prolog的特定学生的平均值,这是我的代码:
score( jason , math101 , 90 ).
score( sami , math102 , 67 ).
score( smith , phys101 , 82 ).
score( sami , chem101 , 88 ).
do(X,S,I) :-
score(X,_,B) ,
write(B) ,
S is S+B ,
I is I+1 ,
write(I) ,
fail.
start :-
read(X) ,
do(X,0,0)
.
我试图使用递归来做,问题是我(代表索引)和S(代表总和)不会增加!我做错了什么?谢谢!
答案 0 :(得分:0)
您的代码存在一些问题:
S is S+B
之类的表达不适用于prolog。一旦变量与一个术语(分配了一个值)统一,就不再是变量:它变成了统一的变量。因此名称统一。
您的do/3
谓词将始终失败。您似乎正在强制通过fail/0
进行回溯,并希望您的计数器随着时间的推移而增加。 Prolog不会那样工作:当你回溯时,统一被取消,所以即使你设法增加你的S
和B
变量,增量也会被回滚当你回过头来。
您正在调用do/3
谓词,其中S
和B
已统一为值(0)。因此,您的is/2
表达式与此完全相同:
0 is 0+B , % S is S+B ,
0 is 0+1 , % I is I+1 ,
你可以看到它是如何运作的。
您需要做的是将每个学生的分数作为列表收集。由于您只能通过回溯获取一个,因此构建该列表有点困难。如果您尝试通过递归构建该列表,则在每次递归时,您会发现自己从零开始。方便的是,Prolog offers a set of predicates for collecting data from all solutions to a goal as a list:
setof/3
找到目标的所有解决方案的集,将其作为列表展示。 findall/3
找到目标所有解决方案的包,并将其作为列表展示。bagof/3
找到目标所有解决方案的包,并将其作为列表展示。注意:findall/3
和bagof/3
相似但不同:引用文档," findall/3
等同于bagof/3
所有自由变量与存在运算符^
"。你可能需要稍微玩一下才能理解这意味着什么。 set 和 bag 之间的区别在于,根据定义, sets 是唯一的,没有重复项。另一方面, Bags 不一定是唯一的,可能包含重复项。
无论如何,您对bagof/3
感兴趣。
要做的第一件事是将您的问题分解为简单的部分。您需要做的第一件事是计算单个学生的平均分数。要做到这一点:
所以,让我们依次解决这个问题。前两个问题通过bagof/3
解决。这样:
bagof(Score,score(Student,_,Score),Scores) .
会找到学生的分数列表,将Student
与学生的姓名统一,将Scores
与个人分数列表统一。在回溯时,它将为所有学生连续做同样的事情。
这解决了前两个简单的问题。第三个问题并不困难。在这里,我们将介绍两个与prolog不相关的概念:递归以及使用 accumulators 来开发一个值。我们必须这样做,因为如前所述,在Prolog中,所有变量都是本地变量,只能赋值一次。因此,为了开发价值,我们必须以递归的方式运行我们需要的状态。一个常见的习语是使用辅助谓词来完成所有工作,并由" public"谓词用户实际使用。
我们可以像这样计算数字列表的算术平均值:
compute_mean( Ns, M ) :- % to compute the mean of a list of numbers,
compute_mean( Ns , 0 , 0 , M ) % - just invoke the helper with the counters seeded as zero.
. % Also easy!
compute_mean( [] , S , N , M ) :- % if the source list is exhausted,
N > 0 , % - and we have a non-zero N (division by zero being undefined),
M is float(S) / float(N) % - compute the arithmetic mean, casting both counters to floats
. % otherwise...
compute_mean( [X|Xs] , S , N , M ) :- % if the source list isn't yet exhausted,
S1 is S+X , % - add the data point to the running total,
N1 is N+1 , % - increment the count of data points,
compute_mean(Xs,S1,N1,M) % - and recurse down on the tail of the list.
. % Easy!
我们需要计算学生的平均分数,并且在回溯时,依次为所有学生计算。把这两件事放在一起吧:
mean_score( Student , Mean ) :- % to compute the mean score for a student,
bagof( Mean , score(Student,_,Score) , Scores ) , % - get the bag of the scores for the student
compute_mean( Scores , Mean ) % - and then compute the mean score
. % Easy!
一旦你有了这个,你就可以这样说:
list_mean_scores_for_all_students :-
mean_score(Student,Mean) ,
write( Student:Mean) , nl ,
fail .
list_mean_scores_for_all_students .
上述问题是list_mean_scores_for_all_students/0
总是成功,即使发生了一些不幸事件。因此,您可能希望使用findall/3
一次性收集它们然后处理它们。
然后我们可以说:
list_mean_scores_for_all_students :-
findall( Student:Mean , mean_score(Student,Mean) , Results ) ,
log_results(Results)
.
log_results( [] ) .
log_results( [S:M|Xs] ) :-
write(S:M),nl ,
log_results(Xs)
.
如果出现问题,list_means_scores_for_all_students/0
将失败。