Построение списка из пользовательского ввода в прологе - PullRequest
0 голосов
/ 25 апреля 2018

Недавно я столкнулся со следующей проблемой в Прологе: Определите предикат avg_num/0, который в интерактивном режиме предлагает пользователю ввести последовательность чисел (или слово «стоп» без кавычек для завершения процесса). Программа выводит среднее из этих чисел.

В терминале / интерпретаторе 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 голосов
/ 25 апреля 2018

Я бы выделил предикат, который читает список, а затем обработал бы список.

Вот один из способов чтения в списке до конца термина: 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 ?-

Тогда вы можете просто использовать стандартные предикаты Пролога для получения среднего значения:

avg_list(L, Avg) :-
    read_list_until(L, stop),
    sum_list(L, Sum),
    length(L, Num),
    Avg is Sum / Num.
0 голосов
/ 25 апреля 2018

Ты почти у цели.Как вы сказали, вам нужно сохранить список входных данных, а затем выполнить окончательное вычисление, как только весь список известен.Такой список (или другой объект, содержащий промежуточные данные) часто называют аккумулятором .

. Вот один из способов реализации этого.Он основан на двух вспомогательных предикатах, один для выполнения части ввода-вывода и один для выполнения вычисления.

Мы начнем вычисление с пустого списка, как вы сказали.

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) ).
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...