Эта лекция посвящена способам организации управления программой при программировании на Прологе. Конечно, какую-то часть способов организации в явном или неявном виде мы уже рассмотрели в предыдущих лекциях. Здесь будет сформулировано явно то, что до сих пор использовалось в неявном виде. Мы разберем, в каких случаях применяются эти методы, как они работают и как ими пользоваться. Также мы рассмотрим некоторые новые методы, которых до сих пор не касались.
Начнем c того, что еще раз обсудим
Во всех точках программы, где существуют альтернативы, в стек заносятся указатели. Если впоследствии окажется, что выбранный вариант не приводит к успеху, то осуществляется
При объяснении сущности
Пример. Рассмотрим работу механизма
DOMAINS /* раздел описания доменов */
s=string /* вводим синоним для строкового типа данных */
PREDICATES /* раздел описания предикатов */
mother(s,s) /* предикат мама будет иметь два аргумента
строкового типа */
grandmother(s,s) /* то же имеет место и для предиката
бабушка */
CLAUSES /* раздел описания предложений */
mother("Даша","Маша"). /* "Даша" и "Маша" связаны
отношением мама */
mother("Наташа","Даша"). /* "Наташа" является мамой
"Даши" */
mother("Наташа","Глаша"). /* "Наташа" и "Глаша" связаны
отношением мама */
mother("Даша","Саша"). /* "Даша" является мамой "Саши" */
grandmother(X,Y):– /* X является бабушкой Y,
если найдется такой Z, что */
mother(X,Z), /* X является мамой Z, а */
mother(Z,Y). /* Z является мамой Y */
В качестве внешней цели, после запуска программы зададим вопрос об именах всех бабушек и внучек (grandmother(B,V)).
Чтобы проследить процесс работы Пролог-системы при поиске ответа на наш вопрос, можно включить трассировку, записав в начале программы
Для выполнения цели grandmother(B,V) должны быть удовлетворены две подцели: mother(B,Z) и mother(Z,V). Первая подцель унифицируется с первым предложением, описывающим отношение "быть мамой". При этом переменная B конкретизируется именем "Даша", а переменная Z — "Маша". В окне трассировки в этот момент результат вычисления текущей подцели (mother("Даша","Маша")), выводящийся после слова RETURN, сопровождается звездочкой (*), которая показывает, что у подцели есть альтернативные решения. Это то самое место, указатель на которое Пролог заносит в стек точек возврата для возможного последующего возвращения.
Затем делается попытка удовлетворить вторую подцель mother(Z,V), причем переменная Z означена именем "Маша". Попытка унифицировать эту подцель с одним из фактов, имеющих отношение к предикату mother, оказывается неудачной. Это происходит потому, что в нашей базе знаний нет никакой информации о детях Маши. О неуспехе говорит слово FAIL в окне трассировки. Происходит B и Z, означенные к моменту
Выбирается вторая альтернатива. Переменная B при этом становится равной имени "Наташа", а переменная Z получает значение "Даша". Звездочка в окне трассировки, как и при первом проходе этой точки, показывает нам, что исчерпаны еще не все из имеющихся альтернатив, удовлетворяющих нашей подцели.
Делается попытка найти решение для второй подцели mother(Z,V) (при Z = "Даша" ). Первое же предложение в процедуре, реализующей предикат mother, унифицируется с текущей подцелью, переменная V получает значение "Маша". Очередная звездочка в окне трассировки отмечает, что указатель на это место помещен в стек точек возврата, для того чтобы вернуться сюда, и что возможны другие означивания для переменной V, приводящие текущую подцель к успеху.
Получаем, что ответ на наш вопрос возможен при следующих значениях переменных: B=Наташа, V=Маша. Этот ответ отображается в окне диалога, после чего осуществляется V, которая уже была означена именем "Маша". Подцель mother(Даша,V) унифицируется с заголовком последнего предложения процедуры, определяющей предикат mother. Переменная V означивается именем "Саша". В диалоговом окне выводится второй возможный ответ на заданный нами в качестве внешней цели вопрос: B=Наташа, V=Саша.
Альтернативных решений для подцели mother(Даша,V) больше нет. Соответственно, в окне трассировки отсутствует звездочка, а в стеке точек возврата нет больше указателя на то место, куда можно было возвращаться для того, чтобы выбирать новые значения для второй подцели правила, определяющего отношение grandmother.
Однако в стеке точек возврата еще остается указатель на тот участок программы, где находились означивания переменных для первой подцели в теле правила, определяющего отношение "быть бабушкой". Пролог-система осуществляет
Первая подцель сопоставляется с третьим фактом mother( "Наташа","Глаша" ). В окне трассировки видим уже знакомый символ звездочки, который свидетельствует о том, что испробованы еще не все возможные варианты для текущей подцели mother(B,Z). Делаются последовательные попытки сопоставить подцель mother("Глаша",V) с одним из фактов, имеющихся в базе знаний. Однако эти попытки заканчиваются неудачей, поскольку наша программа не содержит информации о детях Глаши. В окне трассировки отображается слово FAIL, информирующее нас об этой неудаче.
Процесс выполнения программы в очередной, последний, раз откатывается к тому месту, где можно выбрать решение для первой подцели. Подцель унифицируется с последним предложением в процедуре, описывающей знания, касающиеся мам. Переменная B конкретизируется именем "Даша", а переменная Z — "Cаша". Других вариантов для сопоставления первой подцели не остается. Стек точек возврата пуст. В окне трассировки нет индикатора, сообщающего об альтернативных решениях, к которым возможно возвращение. Пролог-система пытается сопоставить с чем-нибудь вторую подцель mother("Саша",V), однако ей не удается этого сделать. Ни один из фактов не содержит в качестве первого аргумента имя "Саша". Очередная неудача в попытке найти внуков для Даши.
Программа завершается. В диалоговом окне — два найденных в процессе работы решения:
B=Наташа, V=Маша B= Наташа, V=Саша 2 Solutions
Теперь посмотрим, какие имеются у программиста возможности по управлению
Рассмотрим модификацию механизма
Пример. Давайте зададим тот же вопрос, что и в предыдущем примере, но уже не как внешнюю цель, а укажем ее в разделе описания внутренней цели:
GOAL grandmother(B,V)
Если запустить эту программу, она завершит свою работу, так ничего и не выдав в окне диалога. Дело в том, что при наличии в программе внутренней цели Турбо Пролог не отображает в диалоговом окне значений переменных, тогда как при выполнении внешней цели Турбо Пролог выводит в окне значения всех содержащихся в вопросе переменных. Это первое существенное отличие внешней и внутренней цели.
Однако мы можем сами организовать отображение результатов вычисления внутренней цели, добавив к ней в качестве подцелей вывод значений переменных B и V на экран, используя встроенный предикат write. Раздел описания внутренней цели при этом может выглядеть, например, так:
GOAL
grandmother(B,V),write("Имя бабушки — ",B), write(",
имя внучки — ",V),nl
В этом случае мы увидим на экране имена бабушки и внучки, но в окне диалога отобразится не два решения, а всего одно:
Имя бабушки — Наташа, имя внучки — Маша
Дело в том, что при наличии в программе внутренней цели Турбо Пролог находит только одно возможное означивание переменных, а не все возможные, как в случае внешней цели. Это второе отличие внешней и внутренней цели. Если нам необходимо получить все решения, нужно организовать это специально, например, с помощью метода
В методе fail, о котором говорилось в прошлой лекции. Хотя, по большому счету, вместо этого предиката можно воспользоваться каким-нибудь заведомо ложным выражением. Например, 1=2 или чем-то в этом роде.
Если добавить к нашей внутренней цели предикат fail, то получим в окне диалога оба решения, которые мы могли наблюдать, когда задавали этот вопрос, используя внешнюю цель:
Имя бабушки — Наташа, имя внучки — Маша Имя бабушки — Наташа, имя внучки — Саша
Пример. Теперь давайте напишем предикат, который будет выводить на экран с помощью стандартного предиката write имена всех дочек.
show_names:–
mother(_,Name), /* означивает переменную Name
именем дочки */
write(" ", Name), nl,
/* выводит значение переменной
Name на экран */
fail. /* вызывает откат на место, сохраненное
в стеке точек возврата */
Допишем этот предикат к нашей программе про мам и бабушек, не забыв добавить его описание в раздел PREDICATES.
В качестве внутренней цели укажем вывод сообщения "Имена дочек:" (с помощью встроенного предиката write ), переведем курсор на следующую строку стандартным предикатом nl. В качестве третьей подцели запишем предикат show_names, выводящий имена всех дочек.
GOAL
write("Имена дочек:"),nl,
show_names.
Как будет работать эта программа? Сначала будет выведена строка "Имена дочек:", произойдет переход на следующую строку. После этого выполняется подцель show_names.
Во время попытки вычисления этой подцели механизм унификации означивает переменную именем дочери, указанном в качестве второго аргумента в первом предложении процедуры, описывающей предикат mother. Переменная Name получает значение "Маша". При этом в окне трассировки можно видеть звездочку, которая говорит о том, что в стек точек возврата помещен указатель на место, в которое возможен mother(_,Name). Имя "Маша" выводится на экран встроенным предикатом write.
После этого предикат fail вызывает неуспешное завершение правила, затем осуществляется mother(_,Name). В окне диалога будут выведены имена всех дочек в том порядке, в котором они упоминались в нашей программе:
Имена дочек: Маша Даша Глаша Саша
Пример. Давайте изменим наш предикат, чтобы он выводил имена не всех дочек, а только дочек одной мамы. Для этого нужно добавить предикату аргумент, в качестве которого будет указываться имя мамы. Кроме того, в первой подцели требуется заменить анонимную переменную некоторой обычной переменной, которую затем нужно сравнить с именем мамы, указанной в качестве аргумента предиката.
show_names2(Mother):–
mother(M,Name), /* означивает переменную Name
именем дочки мамы Mother */
M=Mother, /* проверяет совпадение имен мам M
и Mother */
write(" ", Name), nl, /* выводит значение
переменной Name
на экран */
fail. /* вызывает откат к месту, сохраненному
в стеке точек возврата */
Вместо первых двух подцелей mother(M,Name), M=Mother можно записать альтернативный вариант: mother(Mother,Name). Результат будет тем же, но процесс вычисления будет отличаться.
Выведем с помощью модифицированного предиката имена дочек Даши. Для этого изменим внутреннюю цель следующим образом:
GOAL
write("Имена дочек Даши:"),nl,
show_names2("Даша").
Отличие в работе этого предиката от предиката, который выводил имена всех дочек, заключаются в следующем. После того, как переменная M будет означена именем очередной мамы, будет проверяться совпадение ее значения с именем "Даша". В случае совпадения будет выведено имя дочери Даши и осуществится "Даша", fail не потребуется, так как подцель M=Mother будет неуспешной сама по себе. Тем не менее, наличие стандартного предиката fail в качестве последней подцели правила необходимо для того, чтобы вызвать M=Mother окажется успешной.
По завершении работы этой программы можно будет увидеть в диалоговом окне результаты:
Имена дочек Даши: Маша Саша
Рассмотрим теперь так называемый метод отсечения и fail (для имитации неудачи и искусственной организации
Пример. Модифицируем еще раз предикат, выводящий имена всех дочек, так чтобы он выводил их не все, а только до определенного имени. У предиката будет один
show_names3(Daughter):–
mother(_,Name), /* означивает переменную Name именем
дочки */
write(" ", Name), nl, /* выводит значение переменной
Name */
Name=Daughter, /* проверяет совпадение имен дочек Name
и Daughter. В случае несовпадения
вызывает откат на место, указатель
на которое хранится в стеке точек
возврата. В случае совпадения, за счет
наличия отсечения, завершает поиск
и вывод имен дочек */
write("Искомый человек найден!"),!.
Этот предикат будет конкретизировать переменную Name именем чьей-то дочери, выводить на экран значение этой переменной. После этого будет сравниваться значение переменной Name со значением переменной Daughter. В случае несовпадения эта подцель терпит неудачу, вызывая show_names3 завершается. То есть
Еще один способ организации повторяющихся действий — так называемый метод повтора, определяемый пользователем. Он также использует
repeat.
repeat:–
repeat.
Этот предикат всегда успешен. После выполнения первого предложения процедуры в стек точек возврата запоминается указатель, поскольку имеется еще один альтернативный вариант для него. В теле второго предложения опять вызывается предикат repeat.
Предикат repeat не является встроенным предикатом, а имя "repeat" — зарезервированным словом. Соответственно, вместо этого имени можно использовать какое-нибудь другое.
С помощью этого предиката можно организовывать циклы, подобные циклам в императивных языках программирования. Нужно не забыть добавить в правило, организующее цикл, кроме предиката repeat, условие завершения цикла и отсечение, чтобы не получилось зацикливания.
Пример. Создадим предикат, который будет дублировать символ, введенный пользователем с клавиатуры. Завершиться этот процесс должен, когда пользователь введет некий ключевой символ, о котором мы заранее договоримся, что его появление означает окончание процесса дублирования символов. Давайте, для определенности, возьмем в качестве такого символа точку (.).
double_char:–
repeat,
readchar(C), /* читаем символ с клавиатуры
в переменную C */
write(C,C), nl,/* выводим на экран значение
переменной C */
C=’.’,!, /* сравниваем значение переменной C
с символом ‘.’*/
nl,write("Была введена точка. Закончили.").
Первой подцелью нашего правила записан вызов предиката repeat. Он обеспечивает нам повторное выполнение следующих за ним подцелей. Можно эксперимента ради закомментировать предикат repeat, дабы убедиться, что в этом случае цикла не получится. Последующие подцели выполнятся всего один раз.
Далее, используя стандартный предикат readchar, осуществляем чтение символа с клавиатуры в переменную C. Посредством встроенного предиката write выводим два экземпляра введенного пользователем символа на экран; стандартным предикатом nl переводим курсор на следующую строку. Затем значение переменной C сравнивается с символом точка ( ‘.’ ). Это условие, которое, с одной стороны, обеспечивает repeat ), а с другой обеспечивает выход из цикла. Если поступивший с клавиатуры символ отличен от точки, подцель C=’.’ терпит неуспех. Пролог-система осуществляет repeat ). Все повторяется еще раз. Пользователь вводит символ, он дважды отображается на экране, сравнивается с точкой. Процесс повторяется до тех пор, пока введенный символ не окажется точкой. В этом случае подцель C=’.’ окажется успешной, управление перейдет на подцели, расположенные правее. Предикат nl сдвинет курсор на начало следующей строки, стандартный предикат write выводит на экран сообщение: "Была введена точка. Закончили." — и процесс дублирования символов на этом завершается.
Напишем внутреннюю цель, которая проинформирует пользователя о правилах работы нашей программы, после чего запустит предикат double_char.
GOAL
write("Вводите символы, которые нужно повторить (точка —
завершение)"),nl,
double_char.
Эта лекция посвящена способам организации управления программой при программировании на Прологе. Конечно, какую-то часть способов организации в явном или неявном виде мы уже рассмотрели в предыдущих лекциях. Здесь будет сформулировано явно то, что до сих пор использовалось в неявном виде. Мы разберем, в каких случаях применяются эти методы, как они работают и как ими пользоваться. Также мы рассмотрим некоторые новые методы, которых до сих пор не касались.
Начнем c того, что еще раз обсудим
Во всех точках программы, где существуют альтернативы, в стек заносятся указатели. Если впоследствии окажется, что выбранный вариант не приводит к успеху, то осуществляется
При объяснении сущности
Пример. Рассмотрим работу механизма
DOMAINS /* раздел описания доменов */
s=string /* вводим синоним для строкового типа данных */
PREDICATES /* раздел описания предикатов */
mother(s,s) /* предикат мама будет иметь два аргумента
строкового типа */
grandmother(s,s) /* то же имеет место и для предиката
бабушка */
CLAUSES /* раздел описания предложений */
mother("Даша","Маша"). /* "Даша" и "Маша" связаны
отношением мама */
mother("Наташа","Даша"). /* "Наташа" является мамой
"Даши" */
mother("Наташа","Глаша"). /* "Наташа" и "Глаша" связаны
отношением мама */
mother("Даша","Саша"). /* "Даша" является мамой "Саши" */
grandmother(X,Y):– /* X является бабушкой Y,
если найдется такой Z, что */
mother(X,Z), /* X является мамой Z, а */
mother(Z,Y). /* Z является мамой Y */
В качестве внешней цели, после запуска программы зададим вопрос об именах всех бабушек и внучек (grandmother(B,V)).
Чтобы проследить процесс работы Пролог-системы при поиске ответа на наш вопрос, можно включить трассировку, записав в начале программы
Для выполнения цели grandmother(B,V) должны быть удовлетворены две подцели: mother(B,Z) и mother(Z,V). Первая подцель унифицируется с первым предложением, описывающим отношение "быть мамой". При этом переменная B конкретизируется именем "Даша", а переменная Z — "Маша". В окне трассировки в этот момент результат вычисления текущей подцели (mother("Даша","Маша")), выводящийся после слова RETURN, сопровождается звездочкой (*), которая показывает, что у подцели есть альтернативные решения. Это то самое место, указатель на которое Пролог заносит в стек точек возврата для возможного последующего возвращения.
Затем делается попытка удовлетворить вторую подцель mother(Z,V), причем переменная Z означена именем "Маша". Попытка унифицировать эту подцель с одним из фактов, имеющих отношение к предикату mother, оказывается неудачной. Это происходит потому, что в нашей базе знаний нет никакой информации о детях Маши. О неуспехе говорит слово FAIL в окне трассировки. Происходит B и Z, означенные к моменту
Выбирается вторая альтернатива. Переменная B при этом становится равной имени "Наташа", а переменная Z получает значение "Даша". Звездочка в окне трассировки, как и при первом проходе этой точки, показывает нам, что исчерпаны еще не все из имеющихся альтернатив, удовлетворяющих нашей подцели.
Делается попытка найти решение для второй подцели mother(Z,V) (при Z = "Даша" ). Первое же предложение в процедуре, реализующей предикат mother, унифицируется с текущей подцелью, переменная V получает значение "Маша". Очередная звездочка в окне трассировки отмечает, что указатель на это место помещен в стек точек возврата, для того чтобы вернуться сюда, и что возможны другие означивания для переменной V, приводящие текущую подцель к успеху.
Получаем, что ответ на наш вопрос возможен при следующих значениях переменных: B=Наташа, V=Маша. Этот ответ отображается в окне диалога, после чего осуществляется V, которая уже была означена именем "Маша". Подцель mother(Даша,V) унифицируется с заголовком последнего предложения процедуры, определяющей предикат mother. Переменная V означивается именем "Саша". В диалоговом окне выводится второй возможный ответ на заданный нами в качестве внешней цели вопрос: B=Наташа, V=Саша.
Альтернативных решений для подцели mother(Даша,V) больше нет. Соответственно, в окне трассировки отсутствует звездочка, а в стеке точек возврата нет больше указателя на то место, куда можно было возвращаться для того, чтобы выбирать новые значения для второй подцели правила, определяющего отношение grandmother.
Однако в стеке точек возврата еще остается указатель на тот участок программы, где находились означивания переменных для первой подцели в теле правила, определяющего отношение "быть бабушкой". Пролог-система осуществляет
Первая подцель сопоставляется с третьим фактом mother( "Наташа","Глаша" ). В окне трассировки видим уже знакомый символ звездочки, который свидетельствует о том, что испробованы еще не все возможные варианты для текущей подцели mother(B,Z). Делаются последовательные попытки сопоставить подцель mother("Глаша",V) с одним из фактов, имеющихся в базе знаний. Однако эти попытки заканчиваются неудачей, поскольку наша программа не содержит информации о детях Глаши. В окне трассировки отображается слово FAIL, информирующее нас об этой неудаче.
Процесс выполнения программы в очередной, последний, раз откатывается к тому месту, где можно выбрать решение для первой подцели. Подцель унифицируется с последним предложением в процедуре, описывающей знания, касающиеся мам. Переменная B конкретизируется именем "Даша", а переменная Z — "Cаша". Других вариантов для сопоставления первой подцели не остается. Стек точек возврата пуст. В окне трассировки нет индикатора, сообщающего об альтернативных решениях, к которым возможно возвращение. Пролог-система пытается сопоставить с чем-нибудь вторую подцель mother("Саша",V), однако ей не удается этого сделать. Ни один из фактов не содержит в качестве первого аргумента имя "Саша". Очередная неудача в попытке найти внуков для Даши.
Программа завершается. В диалоговом окне — два найденных в процессе работы решения:
B=Наташа, V=Маша B= Наташа, V=Саша 2 Solutions
Теперь посмотрим, какие имеются у программиста возможности по управлению
Рассмотрим модификацию механизма
Пример. Давайте зададим тот же вопрос, что и в предыдущем примере, но уже не как внешнюю цель, а укажем ее в разделе описания внутренней цели:
GOAL grandmother(B,V)
Если запустить эту программу, она завершит свою работу, так ничего и не выдав в окне диалога. Дело в том, что при наличии в программе внутренней цели Турбо Пролог не отображает в диалоговом окне значений переменных, тогда как при выполнении внешней цели Турбо Пролог выводит в окне значения всех содержащихся в вопросе переменных. Это первое существенное отличие внешней и внутренней цели.
Однако мы можем сами организовать отображение результатов вычисления внутренней цели, добавив к ней в качестве подцелей вывод значений переменных B и V на экран, используя встроенный предикат write. Раздел описания внутренней цели при этом может выглядеть, например, так:
GOAL
grandmother(B,V),write("Имя бабушки — ",B), write(",
имя внучки — ",V),nl
В этом случае мы увидим на экране имена бабушки и внучки, но в окне диалога отобразится не два решения, а всего одно:
Имя бабушки — Наташа, имя внучки — Маша
Дело в том, что при наличии в программе внутренней цели Турбо Пролог находит только одно возможное означивание переменных, а не все возможные, как в случае внешней цели. Это второе отличие внешней и внутренней цели. Если нам необходимо получить все решения, нужно организовать это специально, например, с помощью метода
В методе fail, о котором говорилось в прошлой лекции. Хотя, по большому счету, вместо этого предиката можно воспользоваться каким-нибудь заведомо ложным выражением. Например, 1=2 или чем-то в этом роде.
Если добавить к нашей внутренней цели предикат fail, то получим в окне диалога оба решения, которые мы могли наблюдать, когда задавали этот вопрос, используя внешнюю цель:
Имя бабушки — Наташа, имя внучки — Маша Имя бабушки — Наташа, имя внучки — Саша
Пример. Теперь давайте напишем предикат, который будет выводить на экран с помощью стандартного предиката write имена всех дочек.
show_names:–
mother(_,Name), /* означивает переменную Name
именем дочки */
write(" ", Name), nl,
/* выводит значение переменной
Name на экран */
fail. /* вызывает откат на место, сохраненное
в стеке точек возврата */
Допишем этот предикат к нашей программе про мам и бабушек, не забыв добавить его описание в раздел PREDICATES.
В качестве внутренней цели укажем вывод сообщения "Имена дочек:" (с помощью встроенного предиката write ), переведем курсор на следующую строку стандартным предикатом nl. В качестве третьей подцели запишем предикат show_names, выводящий имена всех дочек.
GOAL
write("Имена дочек:"),nl,
show_names.
Как будет работать эта программа? Сначала будет выведена строка "Имена дочек:", произойдет переход на следующую строку. После этого выполняется подцель show_names.
Во время попытки вычисления этой подцели механизм унификации означивает переменную именем дочери, указанном в качестве второго аргумента в первом предложении процедуры, описывающей предикат mother. Переменная Name получает значение "Маша". При этом в окне трассировки можно видеть звездочку, которая говорит о том, что в стек точек возврата помещен указатель на место, в которое возможен mother(_,Name). Имя "Маша" выводится на экран встроенным предикатом write.
После этого предикат fail вызывает неуспешное завершение правила, затем осуществляется mother(_,Name). В окне диалога будут выведены имена всех дочек в том порядке, в котором они упоминались в нашей программе:
Имена дочек: Маша Даша Глаша Саша
Пример. Давайте изменим наш предикат, чтобы он выводил имена не всех дочек, а только дочек одной мамы. Для этого нужно добавить предикату аргумент, в качестве которого будет указываться имя мамы. Кроме того, в первой подцели требуется заменить анонимную переменную некоторой обычной переменной, которую затем нужно сравнить с именем мамы, указанной в качестве аргумента предиката.
show_names2(Mother):–
mother(M,Name), /* означивает переменную Name
именем дочки мамы Mother */
M=Mother, /* проверяет совпадение имен мам M
и Mother */
write(" ", Name), nl, /* выводит значение
переменной Name
на экран */
fail. /* вызывает откат к месту, сохраненному
в стеке точек возврата */
Вместо первых двух подцелей mother(M,Name), M=Mother можно записать альтернативный вариант: mother(Mother,Name). Результат будет тем же, но процесс вычисления будет отличаться.
Выведем с помощью модифицированного предиката имена дочек Даши. Для этого изменим внутреннюю цель следующим образом:
GOAL
write("Имена дочек Даши:"),nl,
show_names2("Даша").
Отличие в работе этого предиката от предиката, который выводил имена всех дочек, заключаются в следующем. После того, как переменная M будет означена именем очередной мамы, будет проверяться совпадение ее значения с именем "Даша". В случае совпадения будет выведено имя дочери Даши и осуществится "Даша", fail не потребуется, так как подцель M=Mother будет неуспешной сама по себе. Тем не менее, наличие стандартного предиката fail в качестве последней подцели правила необходимо для того, чтобы вызвать M=Mother окажется успешной.
По завершении работы этой программы можно будет увидеть в диалоговом окне результаты:
Имена дочек Даши: Маша Саша
Рассмотрим теперь так называемый метод отсечения и fail (для имитации неудачи и искусственной организации
Пример. Модифицируем еще раз предикат, выводящий имена всех дочек, так чтобы он выводил их не все, а только до определенного имени. У предиката будет один
show_names3(Daughter):–
mother(_,Name), /* означивает переменную Name именем
дочки */
write(" ", Name), nl, /* выводит значение переменной
Name */
Name=Daughter, /* проверяет совпадение имен дочек Name
и Daughter. В случае несовпадения
вызывает откат на место, указатель
на которое хранится в стеке точек
возврата. В случае совпадения, за счет
наличия отсечения, завершает поиск
и вывод имен дочек */
write("Искомый человек найден!"),!.
Этот предикат будет конкретизировать переменную Name именем чьей-то дочери, выводить на экран значение этой переменной. После этого будет сравниваться значение переменной Name со значением переменной Daughter. В случае несовпадения эта подцель терпит неудачу, вызывая show_names3 завершается. То есть
Еще один способ организации повторяющихся действий — так называемый метод повтора, определяемый пользователем. Он также использует
repeat.
repeat:–
repeat.
Этот предикат всегда успешен. После выполнения первого предложения процедуры в стек точек возврата запоминается указатель, поскольку имеется еще один альтернативный вариант для него. В теле второго предложения опять вызывается предикат repeat.
Предикат repeat не является встроенным предикатом, а имя "repeat" — зарезервированным словом. Соответственно, вместо этого имени можно использовать какое-нибудь другое.
С помощью этого предиката можно организовывать циклы, подобные циклам в императивных языках программирования. Нужно не забыть добавить в правило, организующее цикл, кроме предиката repeat, условие завершения цикла и отсечение, чтобы не получилось зацикливания.
Пример. Создадим предикат, который будет дублировать символ, введенный пользователем с клавиатуры. Завершиться этот процесс должен, когда пользователь введет некий ключевой символ, о котором мы заранее договоримся, что его появление означает окончание процесса дублирования символов. Давайте, для определенности, возьмем в качестве такого символа точку (.).
double_char:–
repeat,
readchar(C), /* читаем символ с клавиатуры
в переменную C */
write(C,C), nl,/* выводим на экран значение
переменной C */
C=’.’,!, /* сравниваем значение переменной C
с символом ‘.’*/
nl,write("Была введена точка. Закончили.").
Первой подцелью нашего правила записан вызов предиката repeat. Он обеспечивает нам повторное выполнение следующих за ним подцелей. Можно эксперимента ради закомментировать предикат repeat, дабы убедиться, что в этом случае цикла не получится. Последующие подцели выполнятся всего один раз.
Далее, используя стандартный предикат readchar, осуществляем чтение символа с клавиатуры в переменную C. Посредством встроенного предиката write выводим два экземпляра введенного пользователем символа на экран; стандартным предикатом nl переводим курсор на следующую строку. Затем значение переменной C сравнивается с символом точка ( ‘.’ ). Это условие, которое, с одной стороны, обеспечивает repeat ), а с другой обеспечивает выход из цикла. Если поступивший с клавиатуры символ отличен от точки, подцель C=’.’ терпит неуспех. Пролог-система осуществляет repeat ). Все повторяется еще раз. Пользователь вводит символ, он дважды отображается на экране, сравнивается с точкой. Процесс повторяется до тех пор, пока введенный символ не окажется точкой. В этом случае подцель C=’.’ окажется успешной, управление перейдет на подцели, расположенные правее. Предикат nl сдвинет курсор на начало следующей строки, стандартный предикат write выводит на экран сообщение: "Была введена точка. Закончили." — и процесс дублирования символов на этом завершается.
Напишем внутреннюю цель, которая проинформирует пользователя о правилах работы нашей программы, после чего запустит предикат double_char.
GOAL
write("Вводите символы, которые нужно повторить (точка —
завершение)"),nl,
double_char.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.