Ты почти у цели.Как вы сказали, вам нужно сохранить список входных данных, а затем выполнить окончательное вычисление, как только весь список известен.Такой список (или другой объект, содержащий промежуточные данные) часто называют аккумулятором .
. Вот один из способов реализации этого.Он основан на двух вспомогательных предикатах, один для выполнения части ввода-вывода и один для выполнения вычисления.
Мы начнем вычисление с пустого списка, как вы сказали.
avg_num :-
avg_num([], Average),
write('The average is '), write(Average), writeln('.').
(Напомним, что в Прологе вы можете перегружать имена предикатов разными арностями, т. Е. avg_num/0
- это отдельный предикат из avg_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/2
и process/3
не обязательно проста для понимания, особенно потому, что имена не очень хороши.osen.
На практике я бы избавился от 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) ).