Пролог: как реализовать сумму квадратов двух наибольших чисел из трех?

Упражнение 1.3 из книги Структура и интерпретация компьютера Программы запрашивает следующее:

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

Я изучаю Пролог. Вот функция, которую я пытался реализовать:

square(X, Y) :- Y is X * X.
squareTwoLargest(X, Y, Z, R) :- 
    R is square(L1) + square(L2), L1 = max(X, Y), L2 = max(min(X, Y), Z).

Однако при запуске выдает следующую ошибку: ERROR: is/2: Arguments are not sufficiently instantiated. Я думаю, что я не только не понимаю синтаксис Пролога, но я еще не понимаю парадигму логического программирования. Итак, как я могу реализовать эту функцию в хорошем стиле логического программирования?


person Marcus Vinícius Monteiro    schedule 13.04.2015    source источник
comment
Поместите = цели перед is   -  person false    schedule 13.04.2015
comment
Оператор is/2 требует, чтобы второй аргумент был полностью реализован. Кроме того, в Прологе есть предикаты, а не функции. предикат завершается успешно или неудачно (или не завершается :)) и не возвращает значение. Однако он может создавать экземпляры аргументов. Таким образом, ваше использование square не сработает. Ваше использование min(X, Y) также будет проблематичным.   -  person lurker    schedule 13.04.2015
comment
@lurker Итак, мне действительно нужно написать R is L1 * L1 + L2 * L2?   -  person Marcus Vinícius Monteiro    schedule 13.04.2015
comment
@false Почему это работает с = перед is (при условии, что я также написал R is L1 * L1 + L2 * L2 вместо использования square?   -  person Marcus Vinícius Monteiro    schedule 13.04.2015
comment
Да, или сделать square(L1, S1), square(L2, S2), R is S1 + S2   -  person lurker    schedule 13.04.2015
comment
Вы можете написать L1^2 вместо square(L1)   -  person false    schedule 13.04.2015


Ответы (3)


К сожалению, функция max, которую вы используете, является встроенной арифметической функцией и не ведет себя как предикат, это может заставить вас думать, что вы будете писать свои предикаты таким же образом.

В Прологе вы будете писать предикаты. Предикат не возвращает никакого значения, он просто удерживает или не удерживает (вы можете думать об этом, как если бы он возвращал true или false). Хорошим примером является ваш предикат square, который на самом деле означает, что square(X,Y) "Y является квадратом X". Если вы спросите консоль Prolog square(4, 16)., она скажет вам true. Если вы спросите square(4, 44), он скажет вам false. Так как же узнать квадратный корень из некоторого числа? Вы задаете Прологу вопрос со свободной (неизвестной) переменной square(4,R)., и Пролог сообщает вам, что R=16. Это важная часть логического программирования, вы не объясняете Прологу, как вычислить квадрат, вы только говорите Прологу, что такое квадрат с точки зрения логики, а затем задаете вопрос Прологу, и он сам найдет ответ.

Так что, если вы попробуете вместо

R is square(L1) + square(L2)

что-то типа

square(L2, L2SQUARED), square(L1, L1SQUARED), ...

что даст вам квадрат L1 в L1SQUARED

Однако L1 не должна быть свободной переменной, Пролог должен иметь возможность вывести для нее некоторое значение на основе некоторых других предикатов (...), чтобы она могла отвечать на square(L1, L1SQUARED). Представьте себе вопрос square(SOMETHING1, SOMETHING2), где оба аргумента неизвестны, каким будет ответ? Существует бесконечное количество правильных ответов, например [2, 4] или [3, 9] и т. д.

Примечание: да, это может быть онлайн с арифметикой, но если вы хотите изучить логическое программирование, попробуйте подход, более похожий на «логическое программирование». В некоторых разновидностях Пролога вы не получаете арифметики, и они все еще полезны...

person Steves    schedule 13.04.2015
comment
Как будет выглядеть более логичный подход к программированию? Это главная причина, по которой я хочу изучать Пролог. - person Marcus Vinícius Monteiro; 13.04.2015
comment
Подход логического программирования заключается в использовании ограничений вместо низкоуровневой арифметики. Ограничения работают во всех направлениях, даже если части выражений еще не известны. Проверьте свою реализацию на наличие функций CLP(Q), CLP(FD) и CLP(R). - person mat; 13.04.2015

Чтобы получить два самых больших числа из трех (V1, V2 и V3), вы можете действовать следующим образом: Отсортируйте список [V1,V2,V3] и возьмите два последних элемента списка [_,X,Y], возведите их в квадрат и просуммируйте.

:- use_module(library(lists)).
:- use_module(library(clpfd)).

squareTwoLargest(V1,V2,V3, R) :- 
    Zs = [_,X,Y],
    chain(Zs, #=<),
    permutation([V1,V2,V3],Zs),
    R #= X*X + Y*Y.

Пример запроса:

?- squareTwoLargest(20,30,10, R).
R = 1300 

Лучшая реализация

Приведенный выше код основан на «сортировке перестановками», что делает его неэффективным более чем одним способом.

Цель squareTwoLargest(X,Y,Z, R) достигается несколько раз и дает избыточные ответы, если два или более из X, Y и Z равны. Об этом свидетельствуют следующие два запроса:

?- squareTwoLargest(0,10,10, R).
R = 200 ;
R = 200 ;
false.

?- squareTwoLargest(10,10,10, R).
R = 200 ;
R = 200 ;
R = 200 ;
R = 200 ;
R = 200 ;
R = 200 ;
false.

Мы можем устранить избыточные ответы, используя сеть сортировки размера 3. Подробнее см. этот ответ на вопрос упорядочение списков с программированием логики ограничений< /а>.

list_sorted__SN3([A0,A1,A2], [D0,D1,C2]) :-
    B1 #= min(A1,A2),   B2 #= max(A1,A2),
    C0 #= min(A0,B2),   C2 #= max(A0,B2),
    D0 #= min(C0,B1),   D1 #= max(C0,B1).

squareTwoLargest__SN(V1,V2,V3, R) :-
    list_sorted__SN3([V1,V2,V3],[_,X,Y]),
    R #= X*X + Y*Y.

Рассмотрим следующие запросы:

?- squareTwoLargest__SN(20,30,10, R).
R = 1300.                              % works like it did before

?- squareTwoLargest__SN(20,20,10, R).
R = 800.                               % succeeds deterministically

?- squareTwoLargest__SN(20,20,20, R).
R = 800.                               % succeeds deterministically 

Обратите внимание, что все избыточные ответы крайних случаев, показанных выше, были удалены.

person repeat    schedule 14.04.2015

моя ставка, используя конструкцию «если-то-иначе».

squareTwoLargest(X, Y, Z, R) :- 
    ( X > Y -> A = X, B = Y ; A = Y, B = X ),
    R is A + max(B, Z).

Нужны две временные переменные.

person CapelliC    schedule 13.04.2015
comment
Ваша исходная функция вернула сумму двух самых больших, а не сумму квадрата двух самых больших (написание R is A * A + max(B, Z) * max(B, Z) сделало свое дело). Я написал «возвращено», потому что начинаю думать, что слово «возвращено» не подходит для использования в Прологе. - person Marcus Vinícius Monteiro; 13.04.2015
comment
ой, я сосредоточился на сложной части (выбор 2 самых больших значений...), поэтому я неправильно понял вопрос. - person CapelliC; 13.04.2015