从Prolog中的用户输入构造列表

时间:2018-04-25 08:27:38

标签: list input prolog

我最近在Prolog中遇到以下问题:定义一个谓词avg_num/0,它以交互方式提示用户输入一系列数字(或单词" stop"不带引号以结束流程)。该程序输出这些数字的平均值。

在Prolog终端/解释器中,程序应该像这样运行:

    ?- avg_sum.
    ?- Enter a number (or stop to end): 2.
    ?- Enter a number (or stop to end): |: 3.
    ?- Enter a number (or stop to end): |: 7.
    ?- Enter a number (or stop to end): |: stop.
    The average is 4.
    true.

我的想法是将值存储到列表中,然后使用以下代码计算该列表的平均数。

    % finding the sum of numbers of a list
    sumlist([],0).
    sumlist([H|T],Sum):- sumlist(T,SumTail), Sum is H + SumTail .

    % finding the average of the numbers in a list
    average(List,A):- sumlist(List,Sum), length(List,Length), A is Sum / Length .

然后将问题简化为:如何将用户输入的数字存储到列表中?不知怎的,我觉得我需要在开始时初始化空列表,但我不知道在哪里放它。我尝试过以下代码(但这不完整):

    avg_sum:- write('Enter a number (or stop to end): '), process(Ans).
    process(Ans):- read(Ans),Ans \= "stop", append(X,[Ans],L). 
    % this part is still incomplete and incorrect.

似乎我需要初始化空列表,但我不知道在哪里。

2 个答案:

答案 0 :(得分:1)

你几乎就在那里。正如您所说,您需要保留一个输入列表,然后在知道整个列表后进行最终计算。这样的列表(或保存中间数据的其他对象)通常称为累加器

以下是实现此目的的一种方法。它基于两个辅助谓词,一个用于执行I / O部分,一个用于执行计算。

我们用空列表开始计算,如你所说。

avg_num :-
    avg_num([], Average),
    write('The average is '), write(Average), writeln('.').

(回想一下,在Prolog中你可以用不同的arities重载谓词名称,即avg_num/0avg_num/2的单独谓词。)

现在avg_num/2可以要求输入并将该输入的处理交给process/3

avg_num(Accumulator, Average) :-
    write('Enter a number (or stop to end): '),
    read(Answer),
    process(Answer, Accumulator, Average).

仅在process/3中,我们通过计算输入stop时的平均值来消耗累加器,或者(如果与stop不同)通过简单地将答案添加到累加器并继续

process(stop, Accumulator, Average) :-
    average(Accumulator, Average).
process(Answer, Accumulator, Average) :-
    dif(Answer, stop),
    avg_num([Answer | Accumulator], Average).

请注意,列表中数字的顺序与计算平均值无关,因此我们可以在列表的前面添加输入,这比在最后附加更容易。

avg_num/2process/3谓词之间的相互递归不一定容易理解,特别是因为名称选择不当。

在实践中,我会通过这样简化process/3来摆脱avg_num/2

avg_num(Accumulator, Average) :-
    write('Enter a number (or stop to end): '),
    read(Answer),
    (   Answer = stop
    ->  average(Accumulator, Average)
    ;   avg_num([Answer | Accumulator], Average) ).

答案 1 :(得分:1)

我会分出一个读取列表的谓词,然后处理列表。

这是在列表中读取的一种方式,直到结束语为End

% read_list_until
%
read_list_until(L, End) :-
    (   read_element(E, End)
    ->  L = [E|L1],
        read_list_until(L1, End)
    ;   L = []
    ).

read_element(E, End) :-
    read(E),
    dif(E, End).

这有以下行为:

2 ?- read_list_until(L, stop).
|: 1.
|: 2.
|: 3.
|: stop.

L = [1, 2, 3].

3 ?-

然后你可以使用标准的Prolog谓词来取平均值:

avg_list(L, Avg) :-
    read_list_until(L, stop),
    sum_list(L, Sum),
    length(L, Num),
    Avg is Sum / Num.