Нужна помощь Понимание чего-то в прологе - PullRequest
3 голосов
/ 28 апреля 2011

Я новичок в Прологе, и ему трудно понять, как работает возврат при использовании правил.Я даже не знаю, работает ли возврат в правилах (хочу знать это правильно).

У меня есть следующая программа, которая суммирует все четные числа в списке.Я написал это сам, но мне было трудно понять, какие шаги нужно предпринять, чтобы найти решение.

evenN(X):- (X mod 2) =:= 0.

sumEven([], 0).
sumEven([H|T], X):- evenN(H), sumEven(T,Y), X is Y+H.
sumEven([H|T], X):- \+evenN(H), sumEven(T,X).

output :::

?- sumEven([1,2,3,4,5,6],X).
X = 12

Нужна помощь, чтобы понять его лучше.Я пытаюсь использовать утилиту трассировки, чтобы понять вывод, но я этого не понимаю, поэтому и спрашиваю здесь.

ВОПРОСЫ:

1)

Когда я закомментирую второе правило (последняя строка), оно дает мне ошибку в качестве ответа, поскольку 1не четное число и вся sumEven () терпит неудачу, потому что evenN () терпит неудачу, что я понимаю.Мой вопрос: что происходит потом?Вернется ли он к началу и попробует факт sumEven ([], 0) или?Я просто хочу знать, что происходит.

2)

Когда включается последняя строка (второе правило), и первое правило не выполняется, когда оно возвращается, ищет ли другое sumEven () который следует за ним (как способ, которым второе правило следует за первым правилом, которое не выполняется), или он возвращается к вершине и проверяет факт sumEven ([], 0) и запускает его оттуда?

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

3) Я нашел следующий код (рекурсивный) в сети.Он делит список на положительные и отрицательные списки.

% predicates
    split(list,list,list)

% clauses
    split([],[],[]).
    split([X|L],[X|L1],L2):-
        X>= 0,
        !,    
        split(L,L1,L2).

    split([X|L],L1,[X|L2]):-
        split(L,L1,L2).

Output :        

Goal: split([1,2,-3,4,-5,2],X,Y)

X=[1,2,4,2], Y=[-3,-5]

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

Ответы [ 2 ]

3 голосов
/ 28 апреля 2011

Соответствующие предложения пробуются в порядке их появления в программе. В хороших декларативных программах Prolog порядок предложений не меняет смысла вашей программы. Это близко к логике, где дизъюнкция коммутативна. У sumEven / 2 есть это свойство. Впрочем, это поразительно неправильно названо, поскольку сумма является вторым аргументом отношения, а не его первым. Лучшее имя будет, например, even_sum / 2, вы можете придумать еще лучшие имена. split / 3 использует! / 0, что уничтожает это свойство и позволяет задавать порядок предложений. По этой причине локальные if-then-else ((->) / 2 и (;) / 2) кажутся более подходящими в этом случае. Вместо trace / 0 используйте графический трассировщик SWI-Prolog с ?- gtrace, your_goal., он также показывает, в каких точках альтернативы еще предстоит попробовать. В общем, подумайте с точки зрения отношений и спросите: когда действует этот пункт? Это верно, если ... и т. Д. Это позволяет рассуждать о гораздо более крупных программах, в которых точный ход выполнения может быть сложнее понять.

2 голосов
/ 28 апреля 2011

Посмотрите, сможете ли вы получить "Программу пролога для искусственного интеллекта" Ивана Братко.

Он объясняет процесс, которым следует Пролог, чтобы очень хорошо достигать целей.

Я постараюсь ответить на ваши вопросы индивидуально:

clause 1: evenN(X):- (X mod 2) =:= 0.
clause 2: sumEven([], 0).
clause 3: sumEven([H|T], X):- evenN(H), sumEven(T,Y), X is Y+H.
clause 4: sumEven([H|T], X):- \+evenN(H), sumEven(T,X).

ВОПРОСЫ:

1) Когда я закомментирую второе правило (последнюю строку), оно дает мне ошибку в качестве ответа, потому что 1 не является четным числом, а весь sumEven () терпит неудачу, потому что evenN () терпит неудачу, что я понимаю. Мой вопрос: что происходит потом? Вернется ли он к началу и попробует факт sumEven ([], 0) или? Я просто хочу знать, что происходит.

A: Вы комментируете пункт 4. Пролог пытается достичь цели, сначала пытаясь выполнить предложение 2, которое не выполняется, поскольку список не пуст, затем он пытается выполнить предложение 3, которое не выполняется по правилу 1 когда Н странно. Теперь он попытается найти другой пункт, следующий за пунктом 3 (он не будет возвращаться к предыдущему пункту 3), который потерпит неудачу, поскольку вы его закомментировали, и поэтому цель не достигнута.

2) Когда включается последняя строка (второе правило), и первое правило не выполняется, когда оно возвращается назад, ищет ли оно другое sumEven (), следующее за ним (подобно тому, как второе правило следует первому правилу, которое не выполняется) ) или он возвращается к началу и проверяет факт sumEven ([], 0) и запускает его оттуда?

A: Нет, он не возвращается, когда пункт 3 не выполняется, но переходит к следующему пункту (4).

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

A : Если правило evenN(H) выполнено успешно, sumEven (T, Y) снова запускает весь процесс из пункта 2, используя T и Y. Если sumEven (T, Y) завершается неудачей по какой-то причине , пункт три завершится неудачно, а Prolog вернется назад и попытается использовать пункт 4. Если текущим вызовом является sumEven ([2,3, ...], X) и по какой-то причине происходит сбой sumEven ([3, ...], Y), Пролог откатится назад и попытается найти другое предложение sumEven ([2,3, ...], X), следующее за пунктом 3.

3) Я нашел следующий код (рекурсивный) в сети. Он делит список на положительные и отрицательные списки.

clause 1:    split([],[],[]).
clause 2:    split([X|L],[X|L1],L2):- X>= 0, !, split(L,L1,L2).
clause 3:    split([X|L],L1,[X|L2]):- split(L,L1,L2).

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

A : я буду использовать более короткое разделение целей (numlist, PosList, NegList) с numlist = [1, -1,2, -2]. Очень грубо это работает следующим образом (на самом деле он использует стек для размещения совпадающих значений и создает переменные только тогда, когда его цель наконец достигает успеха, когда разворачивает этот стек - подробности см. В книге Братко):

Пункт 1 не выполняется, поскольку numlist не пуст.

Пункт 2 применяется с: split ([1 | -1,2, -2], [1 | L1], [Y | L2]) - поскольку 1> = 0 PosList теперь будет [1], и split будет применен к хвосту numlist = [- 1,2, -2].

Пункт 1 снова не выполняется. Пункт 2 применяется с разделением ([- 1 | 2, -2], [- 1 | L1], [Y | L2]) - он завершается неудачей, поскольку -1 <0, и Пролог применит раздел 3 с разделением ([- 1 | 2, -2], [1], [-1 | L2] - NegList теперь будет [-1], и снова разделение будет применено к хвосту numlist = [2, -2]. </p>

Пункт 1 не выполняется; предложение 2 успешно выполняется, PosList становится [1,2], а split применяется к numlist = [- 2].

Пункты 1 и 2 не выполнены; пункт 3 завершается успешно, а NegList становится [-1, -2]. хвост numlist пуст, предложение 1 успешно выполнено, и PosList = [1,2] и NegList [-1, -2] возвращаются.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...