使用prolog的学生的平均值

时间:2014-12-20 19:40:03

标签: prolog

我知道这看起来有点傻,但我试图找到使用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(代表总和)不会增加!我做错了什么?谢谢!

1 个答案:

答案 0 :(得分:0)

您的代码存在一些问题:

  1. S is S+B之类的表达不适用于prolog。一旦变量与一个术语(分配了一个值)统一,就不再是变量:它变成了统一的变量。因此名称​​统一

  2. 您的do/3谓词将始终失败。您似乎正在强制通过fail/0进行回溯,并希望您的计数器随着时间的推移而增加。 Prolog不会那样工作:当你回溯时,统一被取消,所以即使你设法增加你的SB变量,增量也会被回滚当你回过头来。

  3. 您正在调用do/3谓词,其中SB已统一为值(0)。因此,您的is/2表达式与此完全相同:

    0 is 0+B , % S is S+B ,
    0 is 0+1 , % I is I+1 ,
    
  4. 你可以看到它是如何运作的。

    您需要做的是将每个学生的分数作为列表收集。由于您只能通过回溯获取一个,因此构建该列表有点困难。如果您尝试通过递归构建该列表,则在每次递归时,您会发现自己从零开始。方便的是,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/3bagof/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将失败。