Основываясь на комментариях выше и на том факте, что вы все еще новичок в Prolog, я попытаюсь объяснить некоторые вещи, которые могут быть не очень ясными:
What are variables in Prolog and what is instantiation ?
В Прологе переменные - это все, что начинается с заглавных букв и анонимные переменные _
(просто подчеркивание определяет анонимную переменную, которая является переменной без имени). Переменные в Prolog означает, что они могут представлять что угодно: список, строку, атом (... что угодно), когда вы используете новую переменную, она не создается, что означает, что она может представлять что угодно, но прямо сейчас она представляет - она не имеет не быть bind
со списком, атом ... Если вы хотите создать экземпляр переменной, вы можете сделать это:
- используя унификацию
=/2
, например X = 2
унифицирует-создает экземпляр переменной X с номером 2.
- или используя
is/2
только для выражений типа X is 2
, делает то же, что и выше.
Очень важно то, что когда создается переменная в Прологе, мы знаем, что она представляет, и мы не можем изменить, например X =2, X = 3 will fail!!
, поскольку знаем, что X связывается с 2, и мы пытаемся восстановить с 3, что не удается.
What is =/2, is/2, and what's the difference between these two prediactes ??
is/2
: возвращает результат арифметического выражения в неустановленную переменную. Это означает, что вы можете использовать его как: X is 2, or X is mod(Z,Y)
, но здесь важно отметить, что все, что находится в expression MUST be instantiated
, если это ранее были переменные, такие как Y и Z. Если выражение не создано, вы получите instantiation error
, как в вашем случае. Даже простые вещи, такие как 2 is X+1
, выйдут из строя с ошибкой создания экземпляра, если экземпляр X не создан, поэтому не ожидайте, что с is / 2 вернет что-то вроде X=1
в предыдущем случае, что является очевидным ответом (но не уникальным, поэтому он будет непоследовательным!). Также арифметическое выражение означает, что вы не можете использовать что-то вроде X is []
, чтобы объединить X с пустым списком, это, конечно, не удастся, поэтому для этого используйте =/2
, описанный ниже.
=/2
: используется для объединения переменной (или в целом термина) с другим термином. Например, вы можете использовать это как: X = 2
для объединения X с номером 2 или X = [1, 2]
для объединения X со списком или X = 1 + 2
, который не даст 3, а только термин 1 + 2, последние два примера Думаю наглядно показывает разницу с is/2
.
But then how could we use arithmetic expressions like is/2 but more relational ?
Ответ здесь library CLPFD
(как рекомендовано @lurker в комментариях). Все, что вам нужно сделать, это поместить :- use_module(library(clpfd)).
в свой файл и использовать оператор #=
вместо is/2
. Теперь вы можете попробовать X #= 2
, который создаст экземпляр переменной X с номером 2 или даже 2 #= 1 + X
, и теперь вы не получите ошибки создания экземпляра, но получите ответ X = 1
, так что вы можете видеть, что теперь он стал намного более реляционным.
Теперь к вопросу ...
Я согласен с тем, что нейтральный @Boris не следует переносить через рекурсию, а объявлять с функцией. Хотя я постараюсь не отставать от вашей попытки ответа:
Прежде всего, когда я пытаюсь проконсультироваться с файлом с вашим кодом в прологе swi, я получаю:
Warning: /Users/Desktop/reduce.pl:1:
Singleton variables: [F,NEUTRAL,EE]
Это говорит о том, что переменные F, Neutral, EE являются одноэлементными в этом предложении:
reduce([],F,NEUTRAL,EE).
Как видите, в этом предложении вы не используете, например, переменную F.
На мой взгляд, предикат сокращения должен завершиться ошибкой с пустым списком, поскольку вы не можете выполнять никаких операций с нейтральным элементом и функцией, которая принимает два параметра. Итак, в качестве базового случая я бы определил использование списка только с одним элементом:
reduce([H],F,NEUTRAL,EE):-
Goal =.. [F, H, NEUTRAL, EE],
call(Goal).
Затем, как описано выше, действуют такие правила, как:
add(N1,NEUTRAL, EE) :- EE is EE + N1 + NEUTRAL.
вызовет ошибку создания экземпляра, поскольку в EE есть var, и вы не можете использовать его в is / 2, но даже если он был создан, вы также не можете его использовать, потому что, как объяснялось, EE is EE + whatever
не будет работать, потому что you try to reinstantiate a variable..!!
Чтобы исправить это, я бы попытался использовать другую переменную для следующей рекурсии, а затем вычислить результат, например:
reduce([H],F,NEUTRAL,EE):-
Goal =.. [F, H, NEUTRAL, EE],
call(Goal).
reduce([H|T],F,NEUTRAL,EE) :-
reduce(T,F,NEUTRAL,EE1),
Goal =.. [F, H, EE1, EE],
call(Goal).
(Здесь добавлена новая переменная EE1, выполняет рекурсивный вызов, а затем вычисляет результат на основе результата EE1, который будет возвращен из рекурсивного вызова.)
Теперь проверим:
?- reduce([1,2,3,4],add,0,L).
L = 10 ;
false.
?- reduce([1,2,3,4],mult,1,L).
L = 24 ;
false.
Еще одна вещь, когда он выдал результат L=10
нажатием ";" мы запрашиваем больше ответов и возвращает false, поскольку нет возможных ответов. Ложь или истина - это всего лишь способ, которым верхний уровень Пролога информирует вас об успешном или неудачном выполнении предиката, например:
?- reduce([1,2,3,4],mult,1,24).
true ;
false.
Это говорит о том, что приведенное выше верно, и когда мы спрашиваем, есть ли какие-либо другие способы, чтобы это могло быть правдой, он возвращает false ... Так что все, в конце концов, Prolog не кажется таким уж плохим :) (согласно вашему описанию ).
В качестве упражнения вы можете попытаться написать то же самое, используя CLPFD, чтобы быть более реляционным и иметь возможность отвечать на такие вопросы, как: reduce(L,mult,1,24).
и, возможно, идея @Bority о том, чтобы не переносить нейтраль на всем протяжении рекурсии.
ИЗМЕНИТЬ
В соответствии с предложением @false и полезными комментариями я напишу второй способ, используя call / N, который явно лучше (см. Комментарии ...):
reduce([H],F,NEUTRAL,EE):-
call(F, H, NEUTRAL, EE).
reduce([H|T],F,NEUTRAL,EE) :-
reduce(T,F,NEUTRAL,EE1),
call(F, H, EE1, EE).
person
coder
schedule
25.05.2017
Goal =.. [F, H, NEUTRAL, EE], call(Goal)
наcall(F, H, NEUTRAL, EE)
- person false   schedule 24.05.2017reduce([], F, NEUTRAL, EE)
слишком много переменных (вы, вероятно, видели предупреждения синглтона). Этот базовый вариант должен ответить на вопрос Что должно бытьEE
, если список пуст? (И в этом случае вам, вероятно, все равно, что такоеF
иNEUTRAL
.)reduce([], _, _, ??)
.. что такое??
? Это0
? Неправильно созданный экземпляр ошибки возникает, когда вы пытаетесь выполнить вычисление, подобноеEE is N1 * EE * NEUTRAL
, и одна из переменных справа не имеет значения. - person lurker   schedule 24.05.2017EE is N1 * EE * NEUTRAL
может потерпеть неудачу, еслиEE
слева не будет того же значения, что иEE
справа (они такие жеEE
). - person lurker   schedule 24.05.2017EE1 #= N1 * EE * NEUTRAL
. И, да,EE
может быть привязан к значению, его просто нельзя связать снова без возврата. Помните: Prolog не похож на C, C ++ или Java, только с другим синтаксисом. Это язык для определения фактов и правил. Когда переменная связана, Prolog пытается использовать эту привязку, поскольку предикат продолжает выполнение. Чтобы выполнить повторную привязку, должен произойти возврат с возвратом из-за сбоя последующих выражений. - person lurker   schedule 25.05.2017