Пролог - средний предикат: аргументы недостаточно проработаны

У меня есть список автомобилей (auto на немецком языке), где первая переменная - номерной знак, а вторая - скорость:

[auto(eu-ts884, 69), auto(dn-gh184, 64), auto(ac-lj123, 72)].

Теперь я пытаюсь написать средний предикат, но он не может получить сообщение об ошибке:

ОШИБКА: Аргументы недостаточно проработаны

Мой код до сих пор:

durchschnitt([], 0, 0).
durchschnitt([auto(_, X)|Tail], L, Y):-
                        Y is S/L,
                        L > 0,
                        cardinal([auto(_, X)|Tail], L),
                        sumKilometer([auto(_, X)|Tail], S).


sumKilometer([], 0).
sumKilometer([auto(_, X)|Tail], Sum) :-
            sumKilometer(Tail, N),
            Sum is N + X.


cardinal([], 0).
cardinal([_|Tail], Result) :-
  cardinal(Tail, N),
  Result is N + 1.

Мой код вполне эквивалентен этому посту, хотя я не могу разобрать свою ошибку.

Примечание: sumKilometer а также cardinal работают нормально.

1 ответ

Решение

Ты пишешь:

durchschnitt([], 0, 0).
durchschnitt([auto(_, X)|Tail], L, Y):-
    Y is S/L,
    L > 0,
    cardinal([auto(_, X)|Tail], L),
    sumKilometer([auto(_, X)|Tail], S).

Первая проблема заключается в том, что когда вы звоните durchschnitt([auto(foo,2)],L,Y), L является свободной переменной. В результате вы не можете рассчитать Y is S/L так как оба S а также L здесь неизвестны.

Однако вы можете использовать:

durchschnitt([], 0, 0).
durchschnitt([auto(_, X)|Tail], L, Y):-
    cardinal([auto(_, X)|Tail], L),
    sumKilometer([auto(_, X)|Tail], S),
    Y is S/L.

Таким образом, здесь вы рассчитываете среднее значение после обоих L а также S известны. Кроме того, вы не объединяете список с [auto(_,X)|Tail] и т. д. Простая проверка вроде A = [_|_] достаточно:

durchschnitt([], 0, 0).
durchschnitt(A, L, Y):-
    A = [_|_],
    cardinal(A, L),
    sumKilometer(A, S),
    Y is S/L.

Это также сократит время, затрачиваемое на упаковку и распаковку.

Сумма, Длина и Среднее все одновременно

Вы можете создать предикат, который вычисляет все три одновременно (без повторения цикла по списку). Вы можете просто использовать аккумуляторы, такие как:

durchschnitt(A,L,Y) :-
    durchschnitt(A,0,0,L,Y).

Здесь второй и третий элемент - это бегущая сумма и длина соответственно.

Теперь для durchschnitt/5 Есть два случая. В первом случае мы достигли конца списка, и, таким образом, мы должны вычислить среднее значение и вернуть его, например:

durchschnitt([],S,L,L,Y) :-
    (L \= 0
    -> Y is S/L
    ; Y = 0).

Поэтому мы используем if-then-else, чтобы проверить, отличается ли длина от 0 (в случае, если нет auto в списке мы возвращаемся 0 в среднем.

В рекурсивном случае мы просто увеличиваем длину бега и обновляем сумму бега, например:

durchschnitt([auto(_,Si)|T],RS,RL,L,Y) :-
    RSN is RS+Si,
    L1 is L+1,
    durchschnitt(T,RSN,L1,L,Y).

Или положить это вместе:

durchschnitt(A,L,Y) :-
    durchschnitt(A,0,0,L,Y).

durchschnitt([],S,L,L,Y) :-
    (L \= 0
    -> Y is S/L
    ; Y = 0).
durchschnitt([auto(_,Si)|T],RS,RL,L,Y) :-
    RSN is RS+Si,
    L1 is L+1,
    durchschnitt(T,RSN,L1,L,Y).
Другие вопросы по тегам